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
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
con's avatar
con committed
9
** No Commercial Usage
10
**
con's avatar
con committed
11 12 13 14
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
15
**
16
** GNU Lesser General Public License Usage
17
**
18 19 20 21 22 23
** Alternatively, 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.
24
**
con's avatar
con committed
25 26 27 28 29 30
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
con's avatar
con committed
31
**
32
**************************************************************************/
hjk's avatar
hjk committed
33

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

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

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

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

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

#include <locator/commandlocator.h>

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

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

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

87 88 89 90 91 92 93 94 95
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";
96
static const char * const CMD_ID_REVERT_ALL         = "Subversion.RevertAll";
97 98 99 100 101
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";
102
static const char * const CMD_ID_SEPARATOR4         = "Subversion.Separator4";
103
static const char * const CMD_ID_STATUS             = "Subversion.Status";
104 105
static const char * const CMD_ID_PROJECTLOG         = "Subversion.ProjectLog";
static const char * const CMD_ID_REPOSITORYLOG      = "Subversion.RepositoryLog";
106 107 108
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";
109
static const char * const CMD_ID_UPDATE             = "Subversion.Update";
110
static const char * const CMD_ID_COMMIT_PROJECT     = "Subversion.CommitProject";
111
static const char * const CMD_ID_DESCRIBE           = "Subversion.Describe";
con's avatar
con committed
112

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

115 116


con's avatar
con committed
117 118 119
static const VCSBase::VCSBaseEditorParameters editorParameters[] = {
{
    VCSBase::RegularCommandOutput,
120 121
    "Subversion Command Log Editor", // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Command Log Editor"), // display name
122
    "Subversion Command Log Editor", // context
con's avatar
con committed
123 124 125
    "application/vnd.nokia.text.scs_svn_commandlog",
    "scslog"},
{   VCSBase::LogOutput,
126 127
    "Subversion File Log Editor",   // id
    QT_TRANSLATE_NOOP("VCS", "Subversion File Log Editor"),   // display_name
128
    "Subversion File Log Editor",   // context
con's avatar
con committed
129 130 131
    "application/vnd.nokia.text.scs_svn_filelog",
    "scsfilelog"},
{    VCSBase::AnnotateOutput,
132 133
    "Subversion Annotation Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Annotation Editor"),   // display_name
134
    "Subversion Annotation Editor",  // context
con's avatar
con committed
135 136 137
    "application/vnd.nokia.text.scs_svn_annotation",
    "scsannotate"},
{   VCSBase::DiffOutput,
138 139
    "Subversion Diff Editor",  // id
    QT_TRANSLATE_NOOP("VCS", "Subversion Diff Editor"),   // display_name
140
    "Subversion Diff Editor",  // context
con's avatar
con committed
141 142 143 144 145 146 147
    "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);
148
    return  VCSBase::VCSBaseEditorWidget::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et);
con's avatar
con committed
149 150 151 152 153 154 155
}

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

hjk's avatar
hjk committed
156
Core::IEditor* locateEditor(const char *property, const QString &entry)
con's avatar
con committed
157
{
158
    foreach (Core::IEditor *ed, Core::EditorManager::instance()->openedEditors())
con's avatar
con committed
159 160 161 162 163
        if (ed->property(property).toString() == entry)
            return ed;
    return 0;
}

164 165 166 167 168 169 170 171 172 173 174 175 176 177
// 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')) {
178 179
                const QString fileName = line.mid(7); // Column 8 starting from svn 1.6
                changeSet.push_back(SubversionSubmitEditor::StatusFilePair(QString(state), fileName.trimmed()));
180 181 182 183 184 185 186
            }

        }
    }
    return changeSet;
}

187 188 189 190 191 192 193 194 195 196 197
// 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
198 199 200 201
// ------------- SubversionPlugin
SubversionPlugin *SubversionPlugin::m_subversionPluginInstance = 0;

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

SubversionPlugin::~SubversionPlugin()
{
235
    cleanCommitMessageFile();
con's avatar
con committed
236 237
}

238
void SubversionPlugin::cleanCommitMessageFile()
con's avatar
con committed
239
{
240 241 242
    if (!m_commitMessageFileName.isEmpty()) {
        QFile::remove(m_commitMessageFileName);
        m_commitMessageFileName.clear();
243
        m_commitRepository.clear();
con's avatar
con committed
244 245 246
    }
}

247 248 249 250 251
bool SubversionPlugin::isCommitEditorOpen() const
{
    return !m_commitMessageFileName.isEmpty();
}

con's avatar
con committed
252 253
static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = {
    Subversion::Constants::SUBVERSION_SUBMIT_MIMETYPE,
254 255
    Subversion::Constants::SUBVERSIONCOMMITEDITOR_ID,
    Subversion::Constants::SUBVERSIONCOMMITEDITOR_DISPLAY_NAME,
256
    Subversion::Constants::SUBVERSIONCOMMITEDITOR
con's avatar
con committed
257 258
};

259 260 261
static inline Core::Command *createSeparator(QObject *parent,
                                             Core::ActionManager *ami,
                                             const char*id,
262
                                             const Core::Context &globalcontext)
263 264 265 266 267 268
{
    QAction *tmpaction = new QAction(parent);
    tmpaction->setSeparator(true);
    return ami->registerAction(tmpaction, id, globalcontext);
}

269
bool SubversionPlugin::initialize(const QStringList & /*arguments */, QString *errorMessage)
con's avatar
con committed
270 271 272 273 274 275 276 277
{
    typedef VCSBase::VCSSubmitEditorFactory<SubversionSubmitEditor> SubversionSubmitEditorFactory;
    typedef VCSBase::VCSEditorFactory<SubversionEditor> SubversionEditorFactory;
    using namespace Constants;

    using namespace Core::Constants;
    using namespace ExtensionSystem;

278 279
    VCSBase::VCSBasePlugin::initialize(new SubversionControl(this));

con's avatar
con committed
280
    m_subversionPluginInstance = this;
hjk's avatar
hjk committed
281
    Core::ICore *core = Core::ICore::instance();
con's avatar
con committed
282

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

hjk's avatar
hjk committed
286
    if (QSettings *settings = core->settings())
con's avatar
con committed
287 288
        m_settings.fromSettings(settings);

289
    addAutoReleasedObject(new SettingsPage);
con's avatar
con committed
290

291
    addAutoReleasedObject(new SubversionSubmitEditorFactory(&submitParameters));
con's avatar
con committed
292 293 294

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

298 299
    addAutoReleasedObject(new CheckoutWizard);

Friedemann Kleint's avatar
Friedemann Kleint committed
300 301 302 303 304
    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
305
    //register actions
hjk's avatar
hjk committed
306
    Core::ActionManager *ami = core->actionManager();
307
    Core::ActionContainer *toolsContainer = ami->actionContainer(M_TOOLS);
con's avatar
con committed
308

309
    Core::ActionContainer *subversionMenu =
hjk's avatar
hjk committed
310
        ami->createMenu(Core::Id(CMD_ID_SUBVERSION_MENU));
con's avatar
con committed
311 312
    subversionMenu->menu()->setTitle(tr("&Subversion"));
    toolsContainer->addMenu(subversionMenu);
313
    m_menuAction = subversionMenu->menu()->menuAction();
314
    Core::Context globalcontext(C_GLOBAL);
con's avatar
con committed
315
    Core::Command *command;
316 317 318 319

    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
320
    command->setAttribute(Core::Command::CA_UpdateText);
321 322
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+D")));
    connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
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_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
329
    command->setAttribute(Core::Command::CA_UpdateText);
330 331
    connect(m_filelogCurrentAction, SIGNAL(triggered()), this,
        SLOT(filelogCurrentFile()));
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 336 337
    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
338
    command->setAttribute(Core::Command::CA_UpdateText);
339 340
    connect(m_annotateCurrentAction, SIGNAL(triggered()), this,
        SLOT(annotateCurrentFile()));
con's avatar
con committed
341
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
342
    m_commandLocator->appendCommand(command);
con's avatar
con committed
343

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

346 347
    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
348
        globalcontext);
349
    command->setAttribute(Core::Command::CA_UpdateText);
350 351
    command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+A")));
    connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
con's avatar
con committed
352
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
353
    m_commandLocator->appendCommand(command);
con's avatar
con committed
354

355
    m_commitCurrentAction = new Utils::ParameterAction(tr("Commit Current File"), tr("Commit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
con's avatar
con committed
356
    command = ami->registerAction(m_commitCurrentAction,
357
        CMD_ID_COMMIT_CURRENT, globalcontext);
con's avatar
con committed
358
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
359 360 361
    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
362
    m_commandLocator->appendCommand(command);
con's avatar
con committed
363

364 365 366
    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
367
    command->setAttribute(Core::Command::CA_UpdateText);
368
    connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
con's avatar
con committed
369
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
370
    m_commandLocator->appendCommand(command);
con's avatar
con committed
371

372 373 374
    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
375
    command->setAttribute(Core::Command::CA_UpdateText);
376
    connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
con's avatar
con committed
377
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
378
    m_commandLocator->appendCommand(command);
con's avatar
con committed
379

380
    subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR1, globalcontext));
381

382 383 384 385 386 387
    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
388
    m_commandLocator->appendCommand(command);
con's avatar
con committed
389

390 391
    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
392
        globalcontext);
393 394
    command->setAttribute(Core::Command::CA_UpdateText);
    connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(projectStatus()));
con's avatar
con committed
395
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
396
    m_commandLocator->appendCommand(command);
con's avatar
con committed
397

Friedemann Kleint's avatar
Friedemann Kleint committed
398
    m_logProjectAction = new Utils::ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
399 400 401 402
    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
403
    m_commandLocator->appendCommand(command);
404

405
    m_updateProjectAction = new Utils::ParameterAction(tr("Update Project"), tr("Update Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
406
    command = ami->registerAction(m_updateProjectAction, CMD_ID_UPDATE, globalcontext);
con's avatar
con committed
407
    connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateProject()));
408
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
409
    subversionMenu->addAction(command);
Friedemann Kleint's avatar
Friedemann Kleint committed
410
    m_commandLocator->appendCommand(command);
con's avatar
con committed
411

412 413 414 415 416 417 418
    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);

419 420
    subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR2, globalcontext));

421 422 423 424 425 426 427 428 429 430 431 432 433
    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);
434 435 436
    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
437
    m_commandLocator->appendCommand(command);
438

439 440 441
    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()));
442
    subversionMenu->addAction(command);
443
    m_commandLocator->appendCommand(command);
444 445 446 447 448 449

    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
450
    m_commandLocator->appendCommand(command);
451

452 453 454 455 456
    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);

457 458 459 460 461
    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
462
    m_commandLocator->appendCommand(command);
463

con's avatar
con committed
464
    // Actions of the submit editor
465
    Core::Context svncommitcontext(Constants::SUBVERSIONCOMMITEDITOR);
con's avatar
con committed
466

467
    m_submitCurrentLogAction = new QAction(VCSBase::VCSBaseSubmitEditor::submitIcon(), tr("Commit"), this);
con's avatar
con committed
468
    command = ami->registerAction(m_submitCurrentLogAction, Constants::SUBMIT_CURRENT, svncommitcontext);
469
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
470 471
    connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

472
    m_submitDiffAction = new QAction(VCSBase::VCSBaseSubmitEditor::diffIcon(), tr("Diff Selected Files"), this);
con's avatar
con committed
473 474 475 476 477 478 479 480 481 482 483
    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;
}

484
bool SubversionPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
con's avatar
con committed
485
{
486
    if (!isCommitEditorOpen())
con's avatar
con committed
487 488
        return true;

489 490
    Core::IFile *fileIFace = submitEditor->file();
    const SubversionSubmitEditor *editor = qobject_cast<SubversionSubmitEditor *>(submitEditor);
con's avatar
con committed
491 492 493 494 495 496
    if (!fileIFace || !editor)
        return true;

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

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

534
void SubversionPlugin::diffCommitFiles(const QStringList &files)
con's avatar
con committed
535
{
536
    svnDiff(m_commitRepository, files);
con's avatar
con committed
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 574 575 576 577 578 579 580 581 582 583 584 585 586 587
// 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
{
    SubversionDiffParameters() : reUseEditor(false) {}

    QString workingDir;
    QStringList arguments;
    QStringList files;
    QString diffName;
    bool reUseEditor;
};

// 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;
    effectiveParameters.reUseEditor = true;
    // 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);
}

588 589
static inline void setDiffBaseDirectory(Core::IEditor *editor, const QString &db)
{
590
    if (VCSBase::VCSBaseEditorWidget *ve = qobject_cast<VCSBase::VCSBaseEditorWidget*>(editor->widget()))
591 592 593
        ve->setDiffBaseDirectory(db);
}

594
void SubversionPlugin::svnDiff(const QString &workingDir, const QStringList &files, QString diffname)
595 596 597 598 599 600 601 602 603
{
    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
604 605
{
    if (Subversion::Constants::debug)
606 607
        qDebug() << Q_FUNC_INFO << p.files << p.diffName;
    const QString source = VCSBase::VCSBaseEditorWidget::getSource(p.workingDir, p.files);
608
    QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditorWidget::getCodec(source);
con's avatar
con committed
609

610 611
    const QString diffName = p.files.count() == 1 && p.diffName.isEmpty() ?
                             QFileInfo(p.files.front()).fileName() : p.diffName;
con's avatar
con committed
612 613

    QStringList args(QLatin1String("diff"));
614 615
    args.append(p.arguments);
    args << p.files;
con's avatar
con committed
616

617
    const SubversionResponse response =
618
            runSvn(p.workingDir, args, m_settings.timeOutMS(), 0, codec);
con's avatar
con committed
619 620 621 622 623
    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
624
    if (p.files.count() == 1 || p.reUseEditor) {
con's avatar
con committed
625
        // Show in the same editor if diff has been executed before
626
        if (Core::IEditor *editor = locateEditor("originalFileName", p.files.front())) {
con's avatar
con committed
627
            editor->createNew(response.stdOut);
628
            Core::EditorManager::instance()->activateEditor(editor, Core::EditorManager::ModeSwitch);
629
            setDiffBaseDirectory(editor, p.workingDir);
con's avatar
con committed
630 631 632
            return;
        }
    }
633
    const QString title = QString::fromLatin1("svn diff %1").arg(diffName);
con's avatar
con committed
634
    Core::IEditor *editor = showOutputInEditor(title, response.stdOut, VCSBase::DiffOutput, source, codec);
635
    setDiffBaseDirectory(editor, p.workingDir);
636 637
    if (p.files.count() == 1)
        editor->setProperty("originalFileName", p.files.front());
638 639 640 641 642 643 644 645 646 647 648 649
    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
650 651 652 653
}

SubversionSubmitEditor *SubversionPlugin::openSubversionSubmitEditor(const QString &fileName)
{
654 655 656
    Core::IEditor *editor = Core::EditorManager::instance()->openEditor(fileName,
                                                                        QLatin1String(Constants::SUBVERSIONCOMMITEDITOR_ID),
                                                                        Core::EditorManager::ModeSwitch);
con's avatar
con committed
657
    SubversionSubmitEditor *submitEditor = qobject_cast<SubversionSubmitEditor*>(editor);
hjk's avatar
hjk committed
658
    QTC_ASSERT(submitEditor, /**/);
659
    submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_submitCurrentLogAction, m_submitDiffAction);
660
    connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCommitFiles(QStringList)));
661
    submitEditor->setCheckScriptWorkingDirectory(m_commitRepository);
con's avatar
con committed
662 663 664
    return submitEditor;
}

665
void SubversionPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
con's avatar
con committed
666
{
Friedemann Kleint's avatar
Friedemann Kleint committed
667 668
    if (!enableMenuAction(as, m_menuAction)) {
        m_commandLocator->setEnabled(false);
669
        return;
Friedemann Kleint's avatar
Friedemann Kleint committed
670 671 672 673
    }
    const bool hasTopLevel = currentState().hasTopLevel();
    m_commandLocator->setEnabled(hasTopLevel);
    m_logRepositoryAction->setEnabled(hasTopLevel);
674

675 676 677 678
    const QString projectName = currentState().currentProjectName();
    m_diffProjectAction->setParameter(projectName);
    m_statusProjectAction->setParameter(projectName);
    m_updateProjectAction->setParameter(projectName);
679
    m_logProjectAction->setParameter(projectName);
680
    m_commitProjectAction->setParameter(projectName);
681 682 683 684

    const bool repoEnabled = currentState().hasTopLevel();
    m_commitAllAction->setEnabled(repoEnabled);
    m_describeAction->setEnabled(repoEnabled);
685
    m_revertRepositoryAction->setEnabled(repoEnabled);
686 687 688
    m_diffRepositoryAction->setEnabled(repoEnabled);
    m_statusRepositoryAction->setEnabled(repoEnabled);
    m_updateRepositoryAction->setEnabled(repoEnabled);
689 690 691 692 693 694 695 696 697 698

    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
699 700 701 702
}

void SubversionPlugin::addCurrentFile()
{
703 704 705
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
706 707
}

708 709 710 711 712
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
713
    if (QMessageBox::warning(0, title, tr("Revert all pending changes to the repository?"),
714 715 716 717 718
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;
    // NoteL: Svn "revert ." doesn not work.
    QStringList args;
    args << QLatin1String("revert") << QLatin1String("--recursive") << state.topLevel();
719 720 721
    const SubversionResponse revertResponse =
            runSvn(state.topLevel(), args, m_settings.timeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);
722 723 724 725 726 727 728
    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
729 730
void SubversionPlugin::revertCurrentFile()
{
731 732
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
con's avatar
con committed
733 734

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

737 738
    const SubversionResponse diffResponse =
            runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(), 0);
con's avatar
con committed
739 740 741 742 743
    if (diffResponse.error)
        return;

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

748

749
    Core::FileChangeBlocker fcb(state.currentFile());
con's avatar
con committed
750 751 752

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

755 756 757 758
    const SubversionResponse revertResponse =
            runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);

759
    if (!revertResponse.error) {
760
        subVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
con's avatar
con committed
761 762 763 764 765
    }
}

void SubversionPlugin::diffProject()
{
766 767 768
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return)
    svnDiff(state.currentProjectTopLevel(), state.relativeCurrentProject(), state.currentProjectName());
con's avatar
con committed
769 770 771 772
}

void SubversionPlugin::diffCurrentFile()
{
773 774 775
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    svnDiff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
con's avatar
con committed
776 777 778 779
}

void SubversionPlugin::startCommitCurrentFile()
{
780 781 782
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    startCommit(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
con's avatar
con committed
783 784 785 786
}

void SubversionPlugin::startCommitAll()
{
787 788 789
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return);
    startCommit(state.topLevel());
con's avatar
con committed
790 791
}

792 793 794 795 796 797 798
void SubversionPlugin::startCommitProject()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return);
    startCommit(state.currentProjectPath());
}

con's avatar
con committed
799 800 801
/* Start commit of files of a single repository by displaying
 * template and files in a submit editor. On closing, the real
 * commit will start. */
802
void SubversionPlugin::startCommit(const QString &workingDir, const QStringList &files)
con's avatar
con committed
803
{
804 805
    if (VCSBase::VCSBaseSubmitEditor::raiseSubmitEditor())
        return;
806
    if (isCommitEditorOpen()) {
807
        VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("Another commit is currently being executed."));
con's avatar
con committed
808 809 810 811 812 813
        return;
    }

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

814 815
    const SubversionResponse response =
            runSvn(workingDir, args, m_settings.timeOutMS(), 0);
con's avatar
con committed
816 817
    if (response.error)
        return;
818

con's avatar
con committed
819
    // Get list of added/modified/deleted files
820
    const StatusList statusOutput = parseStatusOutput(response.stdOut);
con's avatar
con committed
821
    if (statusOutput.empty()) {
822
        VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("There are no modified files."));
con's avatar
con committed
823 824
        return;
    }
825
    m_commitRepository = workingDir;
con's avatar
con committed
826
    // Create a new submit change file containing the submit template
827 828 829 830
    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
831 832
        return;
    }
833
    m_commitMessageFileName = changeTmpFile.fileName();
834
    // TODO: Regitctrieve submit template from
con's avatar
con committed
835 836
    const QString submitTemplate;
    // Create a submit
837 838 839
    changeTmpFile.write(submitTemplate.toUtf8());
    changeTmpFile.flush();
    changeTmpFile.close();
con's avatar
con committed
840
    // Create a submit editor and set file list
841
    SubversionSubmitEditor *editor = openSubversionSubmitEditor(m_commitMessageFileName);
842
    editor->setStatusList(statusOutput);
con's avatar
con committed
843 844 845 846 847 848 849 850 851 852 853
}

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"));
854
    args << QLatin1String(nonInteractiveOptionC) << QLatin1String("--file") << messageFile;
con's avatar
con committed
855
    args.append(subVersionFileList);
856 857 858
    const SubversionResponse response =
            runSvn(m_commitRepository, args, m_settings.longTimeOutMS(),
                   SshPasswordPrompt|ShowStdOutInLogWindow);
con's avatar
con committed
859 860 861 862 863
    return !response.error ;
}

void SubversionPlugin::filelogCurrentFile()
{
864 865
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
   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
881 882
}

883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
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);
913
    runSvn(workingDir, args, m_settings.timeOutMS(),
914
           ShowStdOutInLogWindow|ShowSuccessMessage);
915 916 917
    outwin->clearRepository();
}

918 919 920
void SubversionPlugin::filelog(const QString &workingDir,
                               const QStringList &files,
                               bool enableAnnotationContextMenu)
con's avatar
con committed
921 922 923
{
    // no need for temp file
    QStringList args(QLatin1String("log"));
924 925
    if (m_settings.logCount > 0)
        args << QLatin1String("-l") << QString::number(m_settings.logCount);
926 927
    foreach(const QString &file, files)
        args.append(QDir::toNativeSeparators(file));
con's avatar
con committed
928