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
        Core::ICore::instance()->fileManager()->saveFile(fileIFace);
516
        closeEditor= commit(m_commitMessageFileName, fileList);
con's avatar
con committed
517
    }
518
    if (closeEditor)
519
        cleanCommitMessageFile();
520
    return closeEditor;
con's avatar
con committed
521 522
}

523
void SubversionPlugin::diffCommitFiles(const QStringList &files)
con's avatar
con committed
524
{
525
    svnDiff(m_commitRepository, files);
con's avatar
con committed
526 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
// 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);
}

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

579
void SubversionPlugin::svnDiff(const QString &workingDir, const QStringList &files, QString diffname)
580 581 582 583 584 585 586 587 588
{
    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
589 590
{
    if (Subversion::Constants::debug)
591 592
        qDebug() << Q_FUNC_INFO << p.files << p.diffName;
    const QString source = VCSBase::VCSBaseEditorWidget::getSource(p.workingDir, p.files);
593
    QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditorWidget::getCodec(source);
con's avatar
con committed
594

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

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

602
    const SubversionResponse response =
603
            runSvn(p.workingDir, args, m_settings.timeOutMS(), 0, codec);
con's avatar
con committed
604 605 606 607 608
    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
609 610 611 612 613 614 615
    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
616
    }
617
    const QString title = QString::fromLatin1("svn diff %1").arg(diffName);
con's avatar
con committed
618
    Core::IEditor *editor = showOutputInEditor(title, response.stdOut, VCSBase::DiffOutput, source, codec);
619
    setDiffBaseDirectory(editor, p.workingDir);
620
    VCSBase::VCSBaseEditorWidget::tagEditor(editor, tag);
621 622 623 624 625 626 627 628 629 630 631 632
    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
633 634 635 636
}

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

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

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

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

    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
682 683 684 685
}

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

691 692 693 694 695
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
696
    if (QMessageBox::warning(0, title, tr("Revert all pending changes to the repository?"),
697 698 699 700 701
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;
    // NoteL: Svn "revert ." doesn not work.
    QStringList args;
    args << QLatin1String("revert") << QLatin1String("--recursive") << state.topLevel();
702 703 704
    const SubversionResponse revertResponse =
            runSvn(state.topLevel(), args, m_settings.timeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);
705 706 707 708 709 710 711
    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
712 713
void SubversionPlugin::revertCurrentFile()
{
714 715
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
con's avatar
con committed
716 717

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

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

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

731

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

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

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

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

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

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

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

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

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

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

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

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

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

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"));
837
    args << QLatin1String(nonInteractiveOptionC) << QLatin1String("--file") << messageFile;
con's avatar
con committed
838
    args.append(subVersionFileList);
839 840 841
    const SubversionResponse response =
            runSvn(m_commitRepository, args, m_settings.longTimeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);
con's avatar
con committed
842 843 844 845 846
    return !response.error ;
}

void SubversionPlugin::filelogCurrentFile()
{
847 848
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
   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
864 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
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);
896
    runSvn(workingDir, args, m_settings.timeOutMS(),
897
           ShowStdOutInLogWindow|ShowSuccessMessage);
898 899 900
    outwin->clearRepository();
}

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

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

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

923
    const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, files);
924 925
    const QString tag = VCSBase::VCSBaseEditorWidget::editorTag(VCSBase::LogOutput, workingDir, files);
    if (Core::IEditor *editor = VCSBase::VCSBaseEditorWidget::locateEditorByTag(tag)) {
con's avatar
con committed
926
        editor->createNew(response.stdOut);
927
        Core::EditorManager::instance()->activateEditor(editor, Core::EditorManager::ModeSwitch);
con's avatar
con committed
928
    } else {
929
        const QString title = QString::fromLatin1("svn log %1").arg(id);
930
        const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files);
Roman Kovalev's avatar
Roman Kovalev committed
931
        Core::IEditor *newEditor = showOutputInEditor(title, response.stdOut, VCSBase::LogOutput, source, /*codec*/0);
932
        VCSBase::VCSBaseEditorWidget::tagEditor(newEditor, tag);
933
        if (enableAnnotationContextMenu)
934
            VCSBase::VCSBaseEditorWidget::getVcsBaseEditor(newEditor)->setFileLogAnnotateEnabled(true);
con's avatar
con committed
935 936 937 938 939
    }
}

void SubversionPlugin::updateProject()
{
940 941
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return);
942 943
    svnUpdate(state.currentProjectTopLevel(), state.relativeCurrentProject());
}
con's avatar
con committed
944

945 946
void SubversionPlugin::svnUpdate(const QString &workingDir, const QStringList &relativePaths)
{
con's avatar
con committed
947
    QStringList args(QLatin1String("update"));
948
    args.push_back(QLatin1String(nonInteractiveOptionC));
949 950
    if (!relativePaths.isEmpty())
        args.append(relativePaths);
951 952 953
        const SubversionResponse response =
                runSvn(workingDir, args, m_settings.longTimeOutMS(),
                       SshPasswordPrompt|ShowStdOutInLogWindow);
954
    if (!response.error)