subversionplugin.cpp 57.5 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28 29
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33 34 35 36 37 38 39 40
#include "subversionplugin.h"

#include "settingspage.h"
#include "subversioneditor.h"

#include "subversionsubmiteditor.h"
#include "subversionconstants.h"
#include "subversioncontrol.h"
41
#include "checkoutwizard.h"
con's avatar
con committed
42 43 44 45

#include <vcsbase/basevcseditorfactory.h>
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/basevcssubmiteditorfactory.h>
46
#include <vcsbase/vcsbaseoutputwindow.h>
47
#include <vcsbase/vcsbaseeditorparameterwidget.h>
con's avatar
con committed
48
#include <utils/synchronousprocess.h>
49
#include <utils/parameteraction.h>
con's avatar
con committed
50 51 52 53 54 55

#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/filemanager.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/mimedatabase.h>
56
#include <coreplugin/actionmanager/actionmanager.h>
57 58
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
59
#include <coreplugin/uniqueidmanager.h>
con's avatar
con committed
60
#include <coreplugin/editormanager/editormanager.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
61 62 63

#include <locator/commandlocator.h>

hjk's avatar
hjk committed
64
#include <utils/qtcassert.h>
con's avatar
con committed
65 66

#include <QtCore/QDebug>
67
#include <QtCore/QDir>
con's avatar
con committed
68 69
#include <QtCore/QFileInfo>
#include <QtCore/QTemporaryFile>
70 71
#include <QtCore/QTextCodec>
#include <QtCore/QtPlugin>
72
#include <QtCore/QProcessEnvironment>
73
#include <QtCore/QUrl>
con's avatar
con committed
74
#include <QtGui/QAction>
75 76
#include <QtGui/QFileDialog>
#include <QtGui/QMainWindow>
con's avatar
con committed
77 78
#include <QtGui/QMenu>
#include <QtGui/QMessageBox>
79
#include <QtGui/QInputDialog>
80
#include <QtXml/QXmlStreamReader>
81
#include <limits.h>
con's avatar
con committed
82

83 84
namespace Subversion {
namespace Internal {
con's avatar
con committed
85

86 87 88 89 90 91 92 93 94
static const char * const CMD_ID_SUBVERSION_MENU    = "Subversion.Menu";
static const char * const CMD_ID_ADD                = "Subversion.Add";
static const char * const CMD_ID_DELETE_FILE        = "Subversion.Delete";
static const char * const CMD_ID_REVERT             = "Subversion.Revert";
static const char * const CMD_ID_SEPARATOR0         = "Subversion.Separator0";
static const char * const CMD_ID_DIFF_PROJECT       = "Subversion.DiffAll";
static const char * const CMD_ID_DIFF_CURRENT       = "Subversion.DiffCurrent";
static const char * const CMD_ID_SEPARATOR1         = "Subversion.Separator1";
static const char * const CMD_ID_COMMIT_ALL         = "Subversion.CommitAll";
95
static const char * const CMD_ID_REVERT_ALL         = "Subversion.RevertAll";
96 97 98 99 100
static const char * const CMD_ID_COMMIT_CURRENT     = "Subversion.CommitCurrent";
static const char * const CMD_ID_SEPARATOR2         = "Subversion.Separator2";
static const char * const CMD_ID_FILELOG_CURRENT    = "Subversion.FilelogCurrent";
static const char * const CMD_ID_ANNOTATE_CURRENT   = "Subversion.AnnotateCurrent";
static const char * const CMD_ID_SEPARATOR3         = "Subversion.Separator3";
101
static const char * const CMD_ID_SEPARATOR4         = "Subversion.Separator4";
102
static const char * const CMD_ID_STATUS             = "Subversion.Status";
103 104
static const char * const CMD_ID_PROJECTLOG         = "Subversion.ProjectLog";
static const char * const CMD_ID_REPOSITORYLOG      = "Subversion.RepositoryLog";
105 106 107
static const char * const CMD_ID_REPOSITORYUPDATE   = "Subversion.RepositoryUpdate";
static const char * const CMD_ID_REPOSITORYDIFF     = "Subversion.RepositoryDiff";
static const char * const CMD_ID_REPOSITORYSTATUS   = "Subversion.RepositoryStatus";
108
static const char * const CMD_ID_UPDATE             = "Subversion.Update";
109
static const char * const CMD_ID_COMMIT_PROJECT     = "Subversion.CommitProject";
110
static const char * const CMD_ID_DESCRIBE           = "Subversion.Describe";
con's avatar
con committed
111

112 113
static const char *nonInteractiveOptionC = "--non-interactive";

114 115


con's avatar
con committed
116 117 118
static const VCSBase::VCSBaseEditorParameters editorParameters[] = {
{
    VCSBase::RegularCommandOutput,
119 120
    "Subversion Command Log Editor", // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Command Log Editor"), // display name
121
    "Subversion Command Log Editor", // context
con's avatar
con committed
122 123 124
    "application/vnd.nokia.text.scs_svn_commandlog",
    "scslog"},
{   VCSBase::LogOutput,
125 126
    "Subversion File Log Editor",   // id
    QT_TRANSLATE_NOOP("VCS", "Subversion File Log Editor"),   // display_name
127
    "Subversion File Log Editor",   // context
con's avatar
con committed
128 129 130
    "application/vnd.nokia.text.scs_svn_filelog",
    "scsfilelog"},
{    VCSBase::AnnotateOutput,
131 132
    "Subversion Annotation Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Annotation Editor"),   // display_name
133
    "Subversion Annotation Editor",  // context
con's avatar
con committed
134 135 136
    "application/vnd.nokia.text.scs_svn_annotation",
    "scsannotate"},
{   VCSBase::DiffOutput,
137 138
    "Subversion Diff Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Diff Editor"),   // display_name
139
    "Subversion Diff Editor",  // context
con's avatar
con committed
140 141 142 143 144 145 146
    "text/x-patch","diff"}
};

// Utility to find a parameter set by type
static inline const VCSBase::VCSBaseEditorParameters *findType(int ie)
{
    const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie);
147
    return  VCSBase::VCSBaseEditorWidget::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et);
con's avatar
con committed
148 149 150 151 152 153 154
}

static inline QString debugCodec(const QTextCodec *c)
{
    return c ? QString::fromAscii(c->name()) : QString::fromAscii("Null codec");
}

155 156 157 158 159 160 161 162 163 164 165 166 167 168
// Parse "svn status" output for added/modified/deleted files
// "M<7blanks>file"
typedef QList<SubversionSubmitEditor::StatusFilePair> StatusList;

StatusList parseStatusOutput(const QString &output)
{
    StatusList changeSet;
    const QString newLine = QString(QLatin1Char('\n'));
    const QStringList list = output.split(newLine, QString::SkipEmptyParts);
    foreach (const QString &l, list) {
        const QString line =l.trimmed();
        if (line.size() > 8) {
            const QChar state = line.at(0);
            if (state == QLatin1Char('A') || state == QLatin1Char('D') || state == QLatin1Char('M')) {
169 170
                const QString fileName = line.mid(7); // Column 8 starting from svn 1.6
                changeSet.push_back(SubversionSubmitEditor::StatusFilePair(QString(state), fileName.trimmed()));
171 172 173 174 175 176 177
            }

        }
    }
    return changeSet;
}

178 179 180 181 182 183 184 185 186 187 188
// Return a list of names for the internal svn directories
static inline QStringList svnDirectories()
{
    QStringList rc(QLatin1String(".svn"));
#ifdef Q_OS_WIN
    // Option on Windows systems to avoid hassle with some IDEs
    rc.push_back(QLatin1String("_svn"));
#endif
    return rc;
}

con's avatar
con committed
189 190 191 192
// ------------- SubversionPlugin
SubversionPlugin *SubversionPlugin::m_subversionPluginInstance = 0;

SubversionPlugin::SubversionPlugin() :
193
    VCSBase::VCSBasePlugin(QLatin1String(Subversion::Constants::SUBVERSIONCOMMITEDITOR_ID)),
194
    m_svnDirectories(svnDirectories()),
Friedemann Kleint's avatar
Friedemann Kleint committed
195
    m_commandLocator(0),
con's avatar
con committed
196 197 198 199 200
    m_addAction(0),
    m_deleteAction(0),
    m_revertAction(0),
    m_diffProjectAction(0),
    m_diffCurrentAction(0),
201 202
    m_logProjectAction(0),
    m_logRepositoryAction(0),
con's avatar
con committed
203
    m_commitAllAction(0),
204
    m_revertRepositoryAction(0),
205 206 207
    m_diffRepositoryAction(0),
    m_statusRepositoryAction(0),
    m_updateRepositoryAction(0),
con's avatar
con committed
208 209 210
    m_commitCurrentAction(0),
    m_filelogCurrentAction(0),
    m_annotateCurrentAction(0),
211
    m_statusProjectAction(0),
con's avatar
con committed
212
    m_updateProjectAction(0),
213
    m_commitProjectAction(0),
214
    m_describeAction(0),
con's avatar
con committed
215 216 217
    m_submitCurrentLogAction(0),
    m_submitDiffAction(0),
    m_submitUndoAction(0),
218
    m_submitRedoAction(0),
219
    m_menuAction(0),
220
    m_submitActionTriggered(false)
con's avatar
con committed
221 222 223 224 225
{
}

SubversionPlugin::~SubversionPlugin()
{
226
    cleanCommitMessageFile();
con's avatar
con committed
227 228
}

229
void SubversionPlugin::cleanCommitMessageFile()
con's avatar
con committed
230
{
231 232 233
    if (!m_commitMessageFileName.isEmpty()) {
        QFile::remove(m_commitMessageFileName);
        m_commitMessageFileName.clear();
234
        m_commitRepository.clear();
con's avatar
con committed
235 236 237
    }
}

238 239 240 241 242
bool SubversionPlugin::isCommitEditorOpen() const
{
    return !m_commitMessageFileName.isEmpty();
}

con's avatar
con committed
243 244
static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = {
    Subversion::Constants::SUBVERSION_SUBMIT_MIMETYPE,
245 246
    Subversion::Constants::SUBVERSIONCOMMITEDITOR_ID,
    Subversion::Constants::SUBVERSIONCOMMITEDITOR_DISPLAY_NAME,
247
    Subversion::Constants::SUBVERSIONCOMMITEDITOR
con's avatar
con committed
248 249
};

250 251 252
static inline Core::Command *createSeparator(QObject *parent,
                                             Core::ActionManager *ami,
                                             const char*id,
253
                                             const Core::Context &globalcontext)
254 255 256 257 258 259
{
    QAction *tmpaction = new QAction(parent);
    tmpaction->setSeparator(true);
    return ami->registerAction(tmpaction, id, globalcontext);
}

260
bool SubversionPlugin::initialize(const QStringList & /*arguments */, QString *errorMessage)
con's avatar
con committed
261 262 263 264 265 266 267 268
{
    typedef VCSBase::VCSSubmitEditorFactory<SubversionSubmitEditor> SubversionSubmitEditorFactory;
    typedef VCSBase::VCSEditorFactory<SubversionEditor> SubversionEditorFactory;
    using namespace Constants;

    using namespace Core::Constants;
    using namespace ExtensionSystem;

269 270
    VCSBase::VCSBasePlugin::initialize(new SubversionControl(this));

con's avatar
con committed
271
    m_subversionPluginInstance = this;
hjk's avatar
hjk committed
272
    Core::ICore *core = Core::ICore::instance();
con's avatar
con committed
273

hjk's avatar
hjk committed
274
    if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.subversion/Subversion.mimetypes.xml"), errorMessage))
con's avatar
con committed
275 276
        return false;

hjk's avatar
hjk committed
277
    if (QSettings *settings = core->settings())
con's avatar
con committed
278 279
        m_settings.fromSettings(settings);

280
    addAutoReleasedObject(new SettingsPage);
con's avatar
con committed
281

282
    addAutoReleasedObject(new SubversionSubmitEditorFactory(&submitParameters));
con's avatar
con committed
283 284 285

    static const char *describeSlot = SLOT(describe(QString,QString));
    const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters);
286 287
    for (int i = 0; i < editorCount; i++)
        addAutoReleasedObject(new SubversionEditorFactory(editorParameters + i, this, describeSlot));
con's avatar
con committed
288

289 290
    addAutoReleasedObject(new CheckoutWizard);

Friedemann Kleint's avatar
Friedemann Kleint committed
291 292 293 294 295
    const QString description = QLatin1String("Subversion");
    const QString prefix = QLatin1String("svn");
    m_commandLocator = new Locator::CommandLocator(description, prefix, prefix);
    addAutoReleasedObject(m_commandLocator);

con's avatar
con committed
296
    //register actions
hjk's avatar
hjk committed
297
    Core::ActionManager *ami = core->actionManager();
298
    Core::ActionContainer *toolsContainer = ami->actionContainer(M_TOOLS);
con's avatar
con committed
299

300
    Core::ActionContainer *subversionMenu =
hjk's avatar
hjk committed
301
        ami->createMenu(Core::Id(CMD_ID_SUBVERSION_MENU));
con's avatar
con committed
302 303
    subversionMenu->menu()->setTitle(tr("&Subversion"));
    toolsContainer->addMenu(subversionMenu);
304
    m_menuAction = subversionMenu->menu()->menuAction();
305
    Core::Context globalcontext(C_GLOBAL);
con's avatar
con committed
306
    Core::Command *command;
307 308 309 310

    m_diffCurrentAction = new Utils::ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_diffCurrentAction,
        CMD_ID_DIFF_CURRENT, globalcontext);
con's avatar
con committed
311
    command->setAttribute(Core::Command::CA_UpdateText);
312 313
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+D")));
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
con's avatar
con committed
314
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
315
    m_commandLocator->appendCommand(command);
con's avatar
con committed
316

317 318 319
    m_filelogCurrentAction = new Utils::ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_filelogCurrentAction,
        CMD_ID_FILELOG_CURRENT, globalcontext);
con's avatar
con committed
320
    command->setAttribute(Core::Command::CA_UpdateText);
321 322
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this,
        SLOT(filelogCurrentFile()));
con's avatar
con committed
323
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
324
    m_commandLocator->appendCommand(command);
con's avatar
con committed
325

326 327 328
    m_annotateCurrentAction = new Utils::ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_annotateCurrentAction,
        CMD_ID_ANNOTATE_CURRENT, globalcontext);
con's avatar
con committed
329
    command->setAttribute(Core::Command::CA_UpdateText);
330 331
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this,
        SLOT(annotateCurrentFile()));
con's avatar
con committed
332
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
333
    m_commandLocator->appendCommand(command);
con's avatar
con committed
334

335
    subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR0, globalcontext));
con's avatar
con committed
336

337 338
    m_addAction = new Utils::ParameterAction(tr("Add"), tr("Add \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_addAction, CMD_ID_ADD,
con's avatar
con committed
339
        globalcontext);
340
    command->setAttribute(Core::Command::CA_UpdateText);
341 342
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+A")));
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
con's avatar
con committed
343
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
344
    m_commandLocator->appendCommand(command);
con's avatar
con committed
345

346
    m_commitCurrentAction = new Utils::ParameterAction(tr("Commit Current File"), tr("Commit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
347
    command = ami->registerAction(m_commitCurrentAction,
348
        CMD_ID_COMMIT_CURRENT, globalcontext);
con's avatar
con committed
349
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
350 351 352
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+C")));
    connect(m_commitCurrentAction, SIGNAL(triggered()), this, SLOT(startCommitCurrentFile()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
353
    m_commandLocator->appendCommand(command);
con's avatar
con committed
354

355 356 357
    m_deleteAction = new Utils::ParameterAction(tr("Delete..."), tr("Delete \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_deleteAction, CMD_ID_DELETE_FILE,
        globalcontext);
con's avatar
con committed
358
    command->setAttribute(Core::Command::CA_UpdateText);
359
    connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
con's avatar
con committed
360
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
361
    m_commandLocator->appendCommand(command);
con's avatar
con committed
362

363 364 365
    m_revertAction = new Utils::ParameterAction(tr("Revert..."), tr("Revert \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_revertAction, CMD_ID_REVERT,
        globalcontext);
con's avatar
con committed
366
    command->setAttribute(Core::Command::CA_UpdateText);
367
    connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
con's avatar
con committed
368
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
369
    m_commandLocator->appendCommand(command);
con's avatar
con committed
370

371
    subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR1, globalcontext));
372

373 374 375 376 377 378
    m_diffProjectAction = new Utils::ParameterAction(tr("Diff Project"), tr("Diff Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_diffProjectAction, CMD_ID_DIFF_PROJECT,
        globalcontext);
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffProject()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
379
    m_commandLocator->appendCommand(command);
con's avatar
con committed
380

381 382
    m_statusProjectAction = new Utils::ParameterAction(tr("Project Status"), tr("Status of Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_statusProjectAction, CMD_ID_STATUS,
con's avatar
con committed
383
        globalcontext);
384 385
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(projectStatus()));
con's avatar
con committed
386
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
387
    m_commandLocator->appendCommand(command);
con's avatar
con committed
388

Friedemann Kleint's avatar
Friedemann Kleint committed
389
    m_logProjectAction = new Utils::ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
390 391 392 393
    command = ami->registerAction(m_logProjectAction, CMD_ID_PROJECTLOG, globalcontext);
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
394
    m_commandLocator->appendCommand(command);
395

396
    m_updateProjectAction = new Utils::ParameterAction(tr("Update Project"), tr("Update Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
397
    command = ami->registerAction(m_updateProjectAction, CMD_ID_UPDATE, globalcontext);
con's avatar
con committed
398
    connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateProject()));
399
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
400
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
401
    m_commandLocator->appendCommand(command);
con's avatar
con committed
402

403 404 405 406 407 408 409
    m_commitProjectAction = new Utils::ParameterAction(tr("Commit Project"), tr("Commit Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
    command = ami->registerAction(m_commitProjectAction, CMD_ID_COMMIT_PROJECT, globalcontext);
    connect(m_commitProjectAction, SIGNAL(triggered()), this, SLOT(startCommitProject()));
    command->setAttribute(Core::Command::CA_UpdateText);
    subversionMenu->addAction(command);
    m_commandLocator->appendCommand(command);

410 411
    subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR2, globalcontext));

412 413 414 415 416 417 418 419 420 421 422 423 424
    m_diffRepositoryAction = new QAction(tr("Diff Repository"), this);
    command = ami->registerAction(m_diffRepositoryAction, CMD_ID_REPOSITORYDIFF, globalcontext);
    connect(m_diffRepositoryAction, SIGNAL(triggered()), this, SLOT(diffRepository()));
    subversionMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_statusRepositoryAction = new QAction(tr("Repository Status"), this);
    command = ami->registerAction(m_statusRepositoryAction, CMD_ID_REPOSITORYSTATUS, globalcontext);
    connect(m_statusRepositoryAction, SIGNAL(triggered()), this, SLOT(statusRepository()));
    subversionMenu->addAction(command);
    m_commandLocator->appendCommand(command);

    m_logRepositoryAction = new QAction(tr("Log Repository"), this);
425 426 427
    command = ami->registerAction(m_logRepositoryAction, CMD_ID_REPOSITORYLOG, globalcontext);
    connect(m_logRepositoryAction, SIGNAL(triggered()), this, SLOT(logRepository()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
428
    m_commandLocator->appendCommand(command);
429

430 431 432
    m_updateRepositoryAction = new QAction(tr("Update Repository"), this);
    command = ami->registerAction(m_updateRepositoryAction, CMD_ID_REPOSITORYUPDATE, globalcontext);
    connect(m_updateRepositoryAction, SIGNAL(triggered()), this, SLOT(updateRepository()));
433
    subversionMenu->addAction(command);
434
    m_commandLocator->appendCommand(command);
435 436 437 438 439 440

    m_commitAllAction = new QAction(tr("Commit All Files"), this);
    command = ami->registerAction(m_commitAllAction, CMD_ID_COMMIT_ALL,
        globalcontext);
    connect(m_commitAllAction, SIGNAL(triggered()), this, SLOT(startCommitAll()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
441
    m_commandLocator->appendCommand(command);
442

443 444 445 446 447
    m_describeAction = new QAction(tr("Describe..."), this);
    command = ami->registerAction(m_describeAction, CMD_ID_DESCRIBE, globalcontext);
    connect(m_describeAction, SIGNAL(triggered()), this, SLOT(slotDescribe()));
    subversionMenu->addAction(command);

448 449 450 451 452
    m_revertRepositoryAction = new QAction(tr("Revert Repository..."), this);
    command = ami->registerAction(m_revertRepositoryAction, CMD_ID_REVERT_ALL,
        globalcontext);
    connect(m_revertRepositoryAction, SIGNAL(triggered()), this, SLOT(revertAll()));
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
453
    m_commandLocator->appendCommand(command);
454

con's avatar
con committed
455
    // Actions of the submit editor
456
    Core::Context svncommitcontext(Constants::SUBVERSIONCOMMITEDITOR);
con's avatar
con committed
457

458
    m_submitCurrentLogAction = new QAction(VCSBase::VCSBaseSubmitEditor::submitIcon(), tr("Commit"), this);
con's avatar
con committed
459
    command = ami->registerAction(m_submitCurrentLogAction, Constants::SUBMIT_CURRENT, svncommitcontext);
460
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
461 462
    connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

463
    m_submitDiffAction = new QAction(VCSBase::VCSBaseSubmitEditor::diffIcon(), tr("Diff Selected Files"), this);
con's avatar
con committed
464 465 466 467 468 469 470 471 472 473 474
    command = ami->registerAction(m_submitDiffAction , Constants::DIFF_SELECTED, svncommitcontext);

    m_submitUndoAction = new QAction(tr("&Undo"), this);
    command = ami->registerAction(m_submitUndoAction, Core::Constants::UNDO, svncommitcontext);

    m_submitRedoAction = new QAction(tr("&Redo"), this);
    command = ami->registerAction(m_submitRedoAction, Core::Constants::REDO, svncommitcontext);

    return true;
}

475
bool SubversionPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
con's avatar
con committed
476
{
477
    if (!isCommitEditorOpen())
con's avatar
con committed
478 479
        return true;

480 481
    Core::IFile *fileIFace = submitEditor->file();
    const SubversionSubmitEditor *editor = qobject_cast<SubversionSubmitEditor *>(submitEditor);
con's avatar
con committed
482 483 484 485 486 487
    if (!fileIFace || !editor)
        return true;

    // Submit editor closing. Make it write out the commit message
    // and retrieve files
    const QFileInfo editorFile(fileIFace->fileName());
488
    const QFileInfo changeFile(m_commitMessageFileName);
con's avatar
con committed
489 490 491
    if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
        return true; // Oops?!

492 493
    // Prompt user. Force a prompt unless submit was actually invoked (that
    // is, the editor was closed or shutdown).
494
    SubversionSettings newSettings = m_settings;
495 496 497
    const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer =
            editor->promptSubmit(tr("Closing Subversion Editor"),
                                 tr("Do you want to commit the change?"),
498
                                 tr("The commit message check failed. Do you want to commit the change?"),
499
                                 &newSettings.promptToSubmit, !m_submitActionTriggered);
500
    m_submitActionTriggered = false;
con's avatar
con committed
501
    switch (answer) {
502
    case VCSBase::VCSBaseSubmitEditor::SubmitCanceled:
con's avatar
con committed
503
        return false; // Keep editing and change file
504
    case VCSBase::VCSBaseSubmitEditor::SubmitDiscarded:
505
        cleanCommitMessageFile();
con's avatar
con committed
506 507 508 509
        return true; // Cancel all
    default:
        break;
    }
510
    setSettings(newSettings); // in case someone turned prompting off
con's avatar
con committed
511
    const QStringList fileList = editor->checkedFiles();
512
    bool closeEditor = true;
con's avatar
con committed
513 514
    if (!fileList.empty()) {
        // get message & commit
515 516 517
        closeEditor = Core::ICore::instance()->fileManager()->saveFile(fileIFace);
        if (closeEditor)
            closeEditor = commit(m_commitMessageFileName, fileList);
con's avatar
con committed
518
    }
519
    if (closeEditor)
520
        cleanCommitMessageFile();
521
    return closeEditor;
con's avatar
con committed
522 523
}

524
void SubversionPlugin::diffCommitFiles(const QStringList &files)
con's avatar
con committed
525
{
526
    svnDiff(m_commitRepository, files);
con's avatar
con committed
527 528
}

529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
// Collect all parameters required for a diff to be able to associate them
// with a diff editor and re-run the diff with parameters.
struct SubversionDiffParameters
{
    QString workingDir;
    QStringList arguments;
    QStringList files;
    QString diffName;
};

// Parameter widget controlling whitespace diff mode, associated with a parameter
class SubversionDiffParameterWidget : public VCSBase::VCSBaseEditorParameterWidget
{
    Q_OBJECT
public:
    explicit SubversionDiffParameterWidget(const SubversionDiffParameters &p, QWidget *parent = 0);

signals:
    void reRunDiff(const Subversion::Internal::SubversionDiffParameters &);

private slots:
    void triggerReRun();

private:
    const SubversionDiffParameters m_parameters;
};

SubversionDiffParameterWidget::SubversionDiffParameterWidget(const SubversionDiffParameters &p, QWidget *parent) :
    VCSBase::VCSBaseEditorParameterWidget(parent), m_parameters(p)
{
    setBaseArguments(p.arguments);
    addIgnoreWhiteSpaceButton(QString(QLatin1Char('w')));
    connect(this, SIGNAL(argumentsChanged()), this, SLOT(triggerReRun()));
}

void SubversionDiffParameterWidget::triggerReRun()
{
    SubversionDiffParameters effectiveParameters = m_parameters;
    // Subversion wants" -x -<ext-args>", default being -u
    const QStringList a = arguments();
    if (!a.isEmpty())
        effectiveParameters.arguments << QLatin1String("-x") << (QLatin1String("-u") + a.join(QString()));
    emit reRunDiff(effectiveParameters);
}

574 575
static inline void setDiffBaseDirectory(Core::IEditor *editor, const QString &db)
{
576
    if (VCSBase::VCSBaseEditorWidget *ve = qobject_cast<VCSBase::VCSBaseEditorWidget*>(editor->widget()))
577 578 579
        ve->setDiffBaseDirectory(db);
}

580
void SubversionPlugin::svnDiff(const QString &workingDir, const QStringList &files, QString diffname)
581 582 583 584 585 586 587 588 589
{
    SubversionDiffParameters p;
    p.workingDir = workingDir;
    p.files = files;
    p.diffName = diffname;
    svnDiff(p);
}

void SubversionPlugin::svnDiff(const Subversion::Internal::SubversionDiffParameters &p)
con's avatar
con committed
590 591
{
    if (Subversion::Constants::debug)
592 593
        qDebug() << Q_FUNC_INFO << p.files << p.diffName;
    const QString source = VCSBase::VCSBaseEditorWidget::getSource(p.workingDir, p.files);
594
    QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditorWidget::getCodec(source);
con's avatar
con committed
595

596 597
    const QString diffName = p.files.count() == 1 && p.diffName.isEmpty() ?
                             QFileInfo(p.files.front()).fileName() : p.diffName;
con's avatar
con committed
598 599

    QStringList args(QLatin1String("diff"));
600 601
    args.append(p.arguments);
    args << p.files;
con's avatar
con committed
602

603
    const SubversionResponse response =
604
            runSvn(p.workingDir, args, m_settings.timeOutMS(), 0, codec);
con's avatar
con committed
605 606 607 608 609
    if (response.error)
        return;

    // diff of a single file? re-use an existing view if possible to support
    // the common usage pattern of continuously changing and diffing a file
610 611 612 613 614 615 616
    const QString tag = VCSBase::VCSBaseEditorWidget::editorTag(VCSBase::DiffOutput, p.workingDir, p.files);
    // Show in the same editor if diff has been executed before
    if (Core::IEditor *existingEditor = VCSBase::VCSBaseEditorWidget::locateEditorByTag(tag)) {
        existingEditor->createNew(response.stdOut);
        Core::EditorManager::instance()->activateEditor(existingEditor, Core::EditorManager::ModeSwitch);
        setDiffBaseDirectory(existingEditor, p.workingDir);
        return;
con's avatar
con committed
617
    }
618
    const QString title = QString::fromLatin1("svn diff %1").arg(diffName);
con's avatar
con committed
619
    Core::IEditor *editor = showOutputInEditor(title, response.stdOut, VCSBase::DiffOutput, source, codec);
620
    setDiffBaseDirectory(editor, p.workingDir);
621
    VCSBase::VCSBaseEditorWidget::tagEditor(editor, tag);
622 623 624 625 626 627 628 629 630 631 632 633
    SubversionEditor *diffEditorWidget = qobject_cast<SubversionEditor *>(editor->widget());
    QTC_ASSERT(diffEditorWidget, return ; )

    // Wire up the parameter widget to trigger a re-run on
    // parameter change and 'revert' from inside the diff editor.
    diffEditorWidget->setRevertDiffChunkEnabled(true);
    SubversionDiffParameterWidget *pw = new SubversionDiffParameterWidget(p);
    connect(pw, SIGNAL(reRunDiff(Subversion::Internal::SubversionDiffParameters)),
            this, SLOT(svnDiff(Subversion::Internal::SubversionDiffParameters)));
    connect(diffEditorWidget, SIGNAL(diffChunkReverted(VCSBase::DiffChunk)),
            pw, SLOT(triggerReRun()));
    diffEditorWidget->setConfigurationWidget(pw);
con's avatar
con committed
634 635 636 637
}

SubversionSubmitEditor *SubversionPlugin::openSubversionSubmitEditor(const QString &fileName)
{
638 639 640
    Core::IEditor *editor = Core::EditorManager::instance()->openEditor(fileName,
                                                                        QLatin1String(Constants::SUBVERSIONCOMMITEDITOR_ID),
                                                                        Core::EditorManager::ModeSwitch);
con's avatar
con committed
641
    SubversionSubmitEditor *submitEditor = qobject_cast<SubversionSubmitEditor*>(editor);
hjk's avatar
hjk committed
642
    QTC_ASSERT(submitEditor, /**/);
643
    submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_submitCurrentLogAction, m_submitDiffAction);
644
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCommitFiles(QStringList)));
645
    submitEditor->setCheckScriptWorkingDirectory(m_commitRepository);
con's avatar
con committed
646 647 648
    return submitEditor;
}

649
void SubversionPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
con's avatar
con committed
650
{
Friedemann Kleint's avatar
Friedemann Kleint committed
651 652
    if (!enableMenuAction(as, m_menuAction)) {
        m_commandLocator->setEnabled(false);
653
        return;
Friedemann Kleint's avatar
Friedemann Kleint committed
654 655 656 657
    }
    const bool hasTopLevel = currentState().hasTopLevel();
    m_commandLocator->setEnabled(hasTopLevel);
    m_logRepositoryAction->setEnabled(hasTopLevel);
658

659 660 661 662
    const QString projectName = currentState().currentProjectName();
    m_diffProjectAction->setParameter(projectName);
    m_statusProjectAction->setParameter(projectName);
    m_updateProjectAction->setParameter(projectName);
663
    m_logProjectAction->setParameter(projectName);
664
    m_commitProjectAction->setParameter(projectName);
665 666 667 668

    const bool repoEnabled = currentState().hasTopLevel();
    m_commitAllAction->setEnabled(repoEnabled);
    m_describeAction->setEnabled(repoEnabled);
669
    m_revertRepositoryAction->setEnabled(repoEnabled);
670 671 672
    m_diffRepositoryAction->setEnabled(repoEnabled);
    m_statusRepositoryAction->setEnabled(repoEnabled);
    m_updateRepositoryAction->setEnabled(repoEnabled);
673 674 675 676 677 678 679 680 681 682

    const QString fileName = currentState().currentFileName();

    m_addAction->setParameter(fileName);
    m_deleteAction->setParameter(fileName);
    m_revertAction->setParameter(fileName);
    m_diffCurrentAction->setParameter(fileName);
    m_commitCurrentAction->setParameter(fileName);
    m_filelogCurrentAction->setParameter(fileName);
    m_annotateCurrentAction->setParameter(fileName);
con's avatar
con committed
683 684 685 686
}

void SubversionPlugin::addCurrentFile()
{
687 688 689
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
690 691
}

692 693 694 695 696
void SubversionPlugin::revertAll()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    const QString title = tr("Revert repository");
Tobias Hunger's avatar
Tobias Hunger committed
697
    if (QMessageBox::warning(0, title, tr("Revert all pending changes to the repository?"),
698 699 700 701 702
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;
    // NoteL: Svn "revert ." doesn not work.
    QStringList args;
    args << QLatin1String("revert") << QLatin1String("--recursive") << state.topLevel();
703 704 705
    const SubversionResponse revertResponse =
            runSvn(state.topLevel(), args, m_settings.timeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);
706 707 708 709 710 711 712
    if (revertResponse.error) {
        QMessageBox::warning(0, title, tr("Revert failed: %1").arg(revertResponse.message), QMessageBox::Ok);
    } else {
        subVersionControl()->emitRepositoryChanged(state.topLevel());
    }
}

con's avatar
con committed
713 714
void SubversionPlugin::revertCurrentFile()
{
715 716
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
con's avatar
con committed
717 718

    QStringList args(QLatin1String("diff"));
719
    args.push_back(state.relativeCurrentFile());
con's avatar
con committed
720

721 722
    const SubversionResponse diffResponse =
            runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(), 0);
con's avatar
con committed
723 724 725 726 727
    if (diffResponse.error)
        return;

    if (diffResponse.stdOut.isEmpty())
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
728
    if (QMessageBox::warning(0, QLatin1String("svn revert"), tr("The file has been changed. Do you want to revert it?"),
con's avatar
con committed
729 730 731
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;

732

733
    Core::FileChangeBlocker fcb(state.currentFile());
con's avatar
con committed
734 735 736

    // revert
    args.clear();
737
    args << QLatin1String("revert") << state.relativeCurrentFile();
con's avatar
con committed
738

739 740 741 742
    const SubversionResponse revertResponse =
            runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);

743
    if (!revertResponse.error) {
744
        subVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
con's avatar
con committed
745 746 747 748 749
    }
}

void SubversionPlugin::diffProject()
{
750 751 752
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return)
    svnDiff(state.currentProjectTopLevel(), state.relativeCurrentProject(), state.currentProjectName());
con's avatar
con committed
753 754 755 756
}

void SubversionPlugin::diffCurrentFile()
{
757 758 759
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    svnDiff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
con's avatar
con committed
760 761 762 763
}

void SubversionPlugin::startCommitCurrentFile()
{
764 765 766
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    startCommit(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
con's avatar
con committed
767 768 769 770
}

void SubversionPlugin::startCommitAll()
{
771 772 773
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return);
    startCommit(state.topLevel());
con's avatar
con committed
774 775
}

776 777 778 779 780 781 782
void SubversionPlugin::startCommitProject()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return);
    startCommit(state.currentProjectPath());
}

con's avatar
con committed
783 784 785
/* Start commit of files of a single repository by displaying
 * template and files in a submit editor. On closing, the real
 * commit will start. */
786
void SubversionPlugin::startCommit(const QString &workingDir, const QStringList &files)
con's avatar
con committed
787
{
788 789
    if (VCSBase::VCSBaseSubmitEditor::raiseSubmitEditor())
        return;
790
    if (isCommitEditorOpen()) {
791
        VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("Another commit is currently being executed."));
con's avatar
con committed
792 793 794 795 796 797
        return;
    }

    QStringList args(QLatin1String("status"));
    args += files;

798 799
    const SubversionResponse response =
            runSvn(workingDir, args, m_settings.timeOutMS(), 0);
con's avatar
con committed
800 801
    if (response.error)
        return;
802

con's avatar
con committed
803
    // Get list of added/modified/deleted files
804
    const StatusList statusOutput = parseStatusOutput(response.stdOut);
con's avatar
con committed
805
    if (statusOutput.empty()) {
806
        VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("There are no modified files."));
con's avatar
con committed
807 808
        return;
    }
809
    m_commitRepository = workingDir;
con's avatar
con committed
810
    // Create a new submit change file containing the submit template
811 812 813 814
    QTemporaryFile changeTmpFile;
    changeTmpFile.setAutoRemove(false);
    if (!changeTmpFile.open()) {
        VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Cannot create temporary file: %1").arg(changeTmpFile.errorString()));
con's avatar
con committed
815 816
        return;
    }
817
    m_commitMessageFileName = changeTmpFile.fileName();
818
    // TODO: Regitctrieve submit template from
con's avatar
con committed
819 820
    const QString submitTemplate;
    // Create a submit
821 822 823
    changeTmpFile.write(submitTemplate.toUtf8());
    changeTmpFile.flush();
    changeTmpFile.close();
con's avatar
con committed
824
    // Create a submit editor and set file list
825
    SubversionSubmitEditor *editor = openSubversionSubmitEditor(m_commitMessageFileName);
826
    editor->setStatusList(statusOutput);
con's avatar
con committed
827 828 829 830 831 832 833 834 835 836 837
}

bool SubversionPlugin::commit(const QString &messageFile,
                              const QStringList &subVersionFileList)
{
    if (Subversion::Constants::debug)
        qDebug() << Q_FUNC_INFO << messageFile << subVersionFileList;
    // Transform the status list which is sth
    // "[ADM]<blanks>file" into an args list. The files of the status log
    // can be relative or absolute depending on where the command was run.
    QStringList args = QStringList(QLatin1String("commit"));
838
    args << QLatin1String(nonInteractiveOptionC) << QLatin1String("--file") << messageFile;
con's avatar
con committed
839
    args.append(subVersionFileList);
840 841 842
    const SubversionResponse response =
            runSvn(m_commitRepository, args, m_settings.longTimeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);
con's avatar
con committed
843 844 845 846 847
    return !response.error ;
}

void SubversionPlugin::filelogCurrentFile()
{
848 849
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
   filelog(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true);
}

void SubversionPlugin::logProject()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return)
    filelog(state.currentProjectTopLevel(), state.relativeCurrentProject());
}

void SubversionPlugin::logRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    filelog(state.topLevel());
con's avatar
con committed
865 866
}

867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
void SubversionPlugin::diffRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    svnDiff(state.topLevel(), QStringList());
}

void SubversionPlugin::statusRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    svnStatus(state.topLevel());
}

void SubversionPlugin::updateRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    svnUpdate(state.topLevel());
}

void SubversionPlugin::svnStatus(const QString &workingDir, const QStringList &relativePaths)
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    QStringList args(QLatin1String("status"));
    if (!relativePaths.isEmpty())
        args.append(relativePaths);
    VCSBase::VCSBaseOutputWindow *outwin = VCSBase::VCSBaseOutputWindow::instance();
    outwin->setRepository(workingDir);
897
    runSvn(workingDir, args, m_settings.timeOutMS(),
898
           ShowStdOutInLogWindow|ShowSuccessMessage);
899 900 901
    outwin->clearRepository();
}

902 903 904
void SubversionPlugin::filelog(const QString &workingDir,
                               const QStringList &files,
                               bool enableAnnotationContextMenu)
con's avatar
con committed
905 906 907
{
    // no need for temp file
    QStringList args(QLatin1String("log"));
908 909
    if (m_settings.logCount > 0)
        args << QLatin1String("-l") << QString::number(m_settings.logCount);
910 911
    foreach(const QString &file, files)
        args.append(QDir::toNativeSeparators(file));
con's avatar
con committed
912

Roman Kovalev's avatar
Roman Kovalev committed
913 914
    // subversion stores log in UTF-8 and returns it back in user system locale.
    // So we do not need to encode it.
915 916
    const SubversionResponse response =
            runSvn(workingDir, args, m_settings.timeOutMS(),
Roman Kovalev's avatar
Roman Kovalev committed
917
                   SshPasswordPrompt, 0/*codec*/);
con's avatar
con committed
918 919 920 921 922 923
    if (response.error)
        return;

    // Re-use an existing view if possible to support
    // the common usage pattern of continuously changing and diffing a file

924
    const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, files);
925 926
    const QString tag = VCSBase::VCSBaseEditorWidget::editorTag(VCSBase::LogOutput, workingDir, files);
    if (Core::IEditor *editor = VCSBase::VCSBaseEditorWidget::locateEditorByTag(tag)) {
con's avatar
con committed
927
        editor->createNew(response.stdOut);
928
        Core::EditorManager::instance()->activateEditor(editor, Core::EditorManager::ModeSwitch);
con's avatar
con committed
929
    } else {
930
        const QString title = QString::fromLatin1("svn log %1").arg(id);