1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "subversionplugin.h"
35 #include "settingspage.h"
36 #include "subversioneditor.h"
38 #include "subversionsubmiteditor.h"
39 #include "subversionconstants.h"
40 #include "subversioncontrol.h"
41 #include "checkoutwizard.h"
43 #include <vcsbase/basevcseditorfactory.h>
44 #include <vcsbase/vcsbaseeditor.h>
45 #include <vcsbase/basevcssubmiteditorfactory.h>
46 #include <vcsbase/vcsbaseoutputwindow.h>
47 #include <utils/synchronousprocess.h>
48 #include <utils/parameteraction.h>
50 #include <coreplugin/icore.h>
51 #include <coreplugin/coreconstants.h>
52 #include <coreplugin/filemanager.h>
53 #include <coreplugin/messagemanager.h>
54 #include <coreplugin/mimedatabase.h>
55 #include <coreplugin/actionmanager/actionmanager.h>
56 #include <coreplugin/actionmanager/actioncontainer.h>
57 #include <coreplugin/actionmanager/command.h>
58 #include <coreplugin/uniqueidmanager.h>
59 #include <coreplugin/editormanager/editormanager.h>
61 #include <locator/commandlocator.h>
63 #include <utils/qtcassert.h>
65 #include <QtCore/QDebug>
66 #include <QtCore/QDir>
67 #include <QtCore/QFileInfo>
68 #include <QtCore/QTemporaryFile>
69 #include <QtCore/QTextCodec>
70 #include <QtCore/QtPlugin>
71 #include <QtCore/QProcessEnvironment>
72 #include <QtCore/QUrl>
73 #include <QtGui/QAction>
74 #include <QtGui/QFileDialog>
75 #include <QtGui/QMainWindow>
76 #include <QtGui/QMenu>
77 #include <QtGui/QMessageBox>
78 #include <QtGui/QInputDialog>
79 #include <QtXml/QXmlStreamReader>
82 using namespace Subversion::Internal;
84 static const char * const CMD_ID_SUBVERSION_MENU = "Subversion.Menu";
85 static const char * const CMD_ID_ADD = "Subversion.Add";
86 static const char * const CMD_ID_DELETE_FILE = "Subversion.Delete";
87 static const char * const CMD_ID_REVERT = "Subversion.Revert";
88 static const char * const CMD_ID_SEPARATOR0 = "Subversion.Separator0";
89 static const char * const CMD_ID_DIFF_PROJECT = "Subversion.DiffAll";
90 static const char * const CMD_ID_DIFF_CURRENT = "Subversion.DiffCurrent";
91 static const char * const CMD_ID_SEPARATOR1 = "Subversion.Separator1";
92 static const char * const CMD_ID_COMMIT_ALL = "Subversion.CommitAll";
93 static const char * const CMD_ID_REVERT_ALL = "Subversion.RevertAll";
94 static const char * const CMD_ID_COMMIT_CURRENT = "Subversion.CommitCurrent";
95 static const char * const CMD_ID_SEPARATOR2 = "Subversion.Separator2";
96 static const char * const CMD_ID_FILELOG_CURRENT = "Subversion.FilelogCurrent";
97 static const char * const CMD_ID_ANNOTATE_CURRENT = "Subversion.AnnotateCurrent";
98 static const char * const CMD_ID_SEPARATOR3 = "Subversion.Separator3";
99 static const char * const CMD_ID_SEPARATOR4 = "Subversion.Separator4";
100 static const char * const CMD_ID_STATUS = "Subversion.Status";
101 static const char * const CMD_ID_PROJECTLOG = "Subversion.ProjectLog";
102 static const char * const CMD_ID_REPOSITORYLOG = "Subversion.RepositoryLog";
103 static const char * const CMD_ID_REPOSITORYUPDATE = "Subversion.RepositoryUpdate";
104 static const char * const CMD_ID_REPOSITORYDIFF = "Subversion.RepositoryDiff";
105 static const char * const CMD_ID_REPOSITORYSTATUS = "Subversion.RepositoryStatus";
106 static const char * const CMD_ID_UPDATE = "Subversion.Update";
107 static const char * const CMD_ID_COMMIT_PROJECT = "Subversion.CommitProject";
108 static const char * const CMD_ID_DESCRIBE = "Subversion.Describe";
110 static const char *nonInteractiveOptionC = "--non-interactive";
114 static const VCSBase::VCSBaseEditorParameters editorParameters[] = {
116 VCSBase::RegularCommandOutput,
117 "Subversion Command Log Editor", // id
118 QT_TRANSLATE_NOOP("VCS", "Subversion Command Log Editor"), // display name
119 "Subversion Command Log Editor", // context
120 "application/vnd.nokia.text.scs_svn_commandlog",
122 { VCSBase::LogOutput,
123 "Subversion File Log Editor", // id
124 QT_TRANSLATE_NOOP("VCS", "Subversion File Log Editor"), // display_name
125 "Subversion File Log Editor", // context
126 "application/vnd.nokia.text.scs_svn_filelog",
128 { VCSBase::AnnotateOutput,
129 "Subversion Annotation Editor", // id
130 QT_TRANSLATE_NOOP("VCS", "Subversion Annotation Editor"), // display_name
131 "Subversion Annotation Editor", // context
132 "application/vnd.nokia.text.scs_svn_annotation",
134 { VCSBase::DiffOutput,
135 "Subversion Diff Editor", // id
136 QT_TRANSLATE_NOOP("VCS", "Subversion Diff Editor"), // display_name
137 "Subversion Diff Editor", // context
138 "text/x-patch","diff"}
141 // Utility to find a parameter set by type
142 static inline const VCSBase::VCSBaseEditorParameters *findType(int ie)
144 const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie);
145 return VCSBase::VCSBaseEditorWidget::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et);
148 static inline QString debugCodec(const QTextCodec *c)
150 return c ? QString::fromAscii(c->name()) : QString::fromAscii("Null codec");
153 Core::IEditor* locateEditor(const char *property, const QString &entry)
155 foreach (Core::IEditor *ed, Core::EditorManager::instance()->openedEditors())
156 if (ed->property(property).toString() == entry)
161 // Parse "svn status" output for added/modified/deleted files
163 typedef QList<SubversionSubmitEditor::StatusFilePair> StatusList;
165 StatusList parseStatusOutput(const QString &output)
167 StatusList changeSet;
168 const QString newLine = QString(QLatin1Char('\n'));
169 const QStringList list = output.split(newLine, QString::SkipEmptyParts);
170 foreach (const QString &l, list) {
171 const QString line =l.trimmed();
172 if (line.size() > 8) {
173 const QChar state = line.at(0);
174 if (state == QLatin1Char('A') || state == QLatin1Char('D') || state == QLatin1Char('M')) {
175 const QString fileName = line.mid(7); // Column 8 starting from svn 1.6
176 changeSet.push_back(SubversionSubmitEditor::StatusFilePair(QString(state), fileName.trimmed()));
184 // Return a list of names for the internal svn directories
185 static inline QStringList svnDirectories()
187 QStringList rc(QLatin1String(".svn"));
189 // Option on Windows systems to avoid hassle with some IDEs
190 rc.push_back(QLatin1String("_svn"));
195 // ------------- SubversionPlugin
196 SubversionPlugin *SubversionPlugin::m_subversionPluginInstance = 0;
198 SubversionPlugin::SubversionPlugin() :
199 VCSBase::VCSBasePlugin(QLatin1String(Subversion::Constants::SUBVERSIONCOMMITEDITOR_ID)),
200 m_svnDirectories(svnDirectories()),
205 m_diffProjectAction(0),
206 m_diffCurrentAction(0),
207 m_logProjectAction(0),
208 m_logRepositoryAction(0),
209 m_commitAllAction(0),
210 m_revertRepositoryAction(0),
211 m_diffRepositoryAction(0),
212 m_statusRepositoryAction(0),
213 m_updateRepositoryAction(0),
214 m_commitCurrentAction(0),
215 m_filelogCurrentAction(0),
216 m_annotateCurrentAction(0),
217 m_statusProjectAction(0),
218 m_updateProjectAction(0),
219 m_commitProjectAction(0),
221 m_submitCurrentLogAction(0),
222 m_submitDiffAction(0),
223 m_submitUndoAction(0),
224 m_submitRedoAction(0),
226 m_submitActionTriggered(false)
230 SubversionPlugin::~SubversionPlugin()
232 cleanCommitMessageFile();
235 void SubversionPlugin::cleanCommitMessageFile()
237 if (!m_commitMessageFileName.isEmpty()) {
238 QFile::remove(m_commitMessageFileName);
239 m_commitMessageFileName.clear();
240 m_commitRepository.clear();
244 bool SubversionPlugin::isCommitEditorOpen() const
246 return !m_commitMessageFileName.isEmpty();
249 static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = {
250 Subversion::Constants::SUBVERSION_SUBMIT_MIMETYPE,
251 Subversion::Constants::SUBVERSIONCOMMITEDITOR_ID,
252 Subversion::Constants::SUBVERSIONCOMMITEDITOR_DISPLAY_NAME,
253 Subversion::Constants::SUBVERSIONCOMMITEDITOR
256 static inline Core::Command *createSeparator(QObject *parent,
257 Core::ActionManager *ami,
259 const Core::Context &globalcontext)
261 QAction *tmpaction = new QAction(parent);
262 tmpaction->setSeparator(true);
263 return ami->registerAction(tmpaction, id, globalcontext);
266 bool SubversionPlugin::initialize(const QStringList & /*arguments */, QString *errorMessage)
268 typedef VCSBase::VCSSubmitEditorFactory<SubversionSubmitEditor> SubversionSubmitEditorFactory;
269 typedef VCSBase::VCSEditorFactory<SubversionEditor> SubversionEditorFactory;
270 using namespace Constants;
272 using namespace Core::Constants;
273 using namespace ExtensionSystem;
275 VCSBase::VCSBasePlugin::initialize(new SubversionControl(this));
277 m_subversionPluginInstance = this;
278 Core::ICore *core = Core::ICore::instance();
280 if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.subversion/Subversion.mimetypes.xml"), errorMessage))
283 if (QSettings *settings = core->settings())
284 m_settings.fromSettings(settings);
286 addAutoReleasedObject(new SettingsPage);
288 addAutoReleasedObject(new SubversionSubmitEditorFactory(&submitParameters));
290 static const char *describeSlot = SLOT(describe(QString,QString));
291 const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters);
292 for (int i = 0; i < editorCount; i++)
293 addAutoReleasedObject(new SubversionEditorFactory(editorParameters + i, this, describeSlot));
295 addAutoReleasedObject(new CheckoutWizard);
297 const QString description = QLatin1String("Subversion");
298 const QString prefix = QLatin1String("svn");
299 m_commandLocator = new Locator::CommandLocator(description, prefix, prefix);
300 addAutoReleasedObject(m_commandLocator);
303 Core::ActionManager *ami = core->actionManager();
304 Core::ActionContainer *toolsContainer = ami->actionContainer(M_TOOLS);
306 Core::ActionContainer *subversionMenu =
307 ami->createMenu(Core::Id(CMD_ID_SUBVERSION_MENU));
308 subversionMenu->menu()->setTitle(tr("&Subversion"));
309 toolsContainer->addMenu(subversionMenu);
310 m_menuAction = subversionMenu->menu()->menuAction();
311 Core::Context globalcontext(C_GLOBAL);
312 Core::Command *command;
314 m_diffCurrentAction = new Utils::ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
315 command = ami->registerAction(m_diffCurrentAction,
316 CMD_ID_DIFF_CURRENT, globalcontext);
317 command->setAttribute(Core::Command::CA_UpdateText);
318 command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+D")));
319 connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
320 subversionMenu->addAction(command);
321 m_commandLocator->appendCommand(command);
323 m_filelogCurrentAction = new Utils::ParameterAction(tr("Filelog Current File"), tr("Filelog \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
324 command = ami->registerAction(m_filelogCurrentAction,
325 CMD_ID_FILELOG_CURRENT, globalcontext);
326 command->setAttribute(Core::Command::CA_UpdateText);
327 connect(m_filelogCurrentAction, SIGNAL(triggered()), this,
328 SLOT(filelogCurrentFile()));
329 subversionMenu->addAction(command);
330 m_commandLocator->appendCommand(command);
332 m_annotateCurrentAction = new Utils::ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
333 command = ami->registerAction(m_annotateCurrentAction,
334 CMD_ID_ANNOTATE_CURRENT, globalcontext);
335 command->setAttribute(Core::Command::CA_UpdateText);
336 connect(m_annotateCurrentAction, SIGNAL(triggered()), this,
337 SLOT(annotateCurrentFile()));
338 subversionMenu->addAction(command);
339 m_commandLocator->appendCommand(command);
341 subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR0, globalcontext));
343 m_addAction = new Utils::ParameterAction(tr("Add"), tr("Add \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
344 command = ami->registerAction(m_addAction, CMD_ID_ADD,
346 command->setAttribute(Core::Command::CA_UpdateText);
347 command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+A")));
348 connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile()));
349 subversionMenu->addAction(command);
350 m_commandLocator->appendCommand(command);
352 m_commitCurrentAction = new Utils::ParameterAction(tr("Commit Current File"), tr("Commit \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
353 command = ami->registerAction(m_commitCurrentAction,
354 CMD_ID_COMMIT_CURRENT, globalcontext);
355 command->setAttribute(Core::Command::CA_UpdateText);
356 command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+C")));
357 connect(m_commitCurrentAction, SIGNAL(triggered()), this, SLOT(startCommitCurrentFile()));
358 subversionMenu->addAction(command);
359 m_commandLocator->appendCommand(command);
361 m_deleteAction = new Utils::ParameterAction(tr("Delete..."), tr("Delete \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
362 command = ami->registerAction(m_deleteAction, CMD_ID_DELETE_FILE,
364 command->setAttribute(Core::Command::CA_UpdateText);
365 connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(promptToDeleteCurrentFile()));
366 subversionMenu->addAction(command);
367 m_commandLocator->appendCommand(command);
369 m_revertAction = new Utils::ParameterAction(tr("Revert..."), tr("Revert \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
370 command = ami->registerAction(m_revertAction, CMD_ID_REVERT,
372 command->setAttribute(Core::Command::CA_UpdateText);
373 connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
374 subversionMenu->addAction(command);
375 m_commandLocator->appendCommand(command);
377 subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR1, globalcontext));
379 m_diffProjectAction = new Utils::ParameterAction(tr("Diff Project"), tr("Diff Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
380 command = ami->registerAction(m_diffProjectAction, CMD_ID_DIFF_PROJECT,
382 command->setAttribute(Core::Command::CA_UpdateText);
383 connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffProject()));
384 subversionMenu->addAction(command);
385 m_commandLocator->appendCommand(command);
387 m_statusProjectAction = new Utils::ParameterAction(tr("Project Status"), tr("Status of Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
388 command = ami->registerAction(m_statusProjectAction, CMD_ID_STATUS,
390 command->setAttribute(Core::Command::CA_UpdateText);
391 connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(projectStatus()));
392 subversionMenu->addAction(command);
393 m_commandLocator->appendCommand(command);
395 m_logProjectAction = new Utils::ParameterAction(tr("Log Project"), tr("Log Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
396 command = ami->registerAction(m_logProjectAction, CMD_ID_PROJECTLOG, globalcontext);
397 command->setAttribute(Core::Command::CA_UpdateText);
398 connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject()));
399 subversionMenu->addAction(command);
400 m_commandLocator->appendCommand(command);
402 m_updateProjectAction = new Utils::ParameterAction(tr("Update Project"), tr("Update Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
403 command = ami->registerAction(m_updateProjectAction, CMD_ID_UPDATE, globalcontext);
404 connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateProject()));
405 command->setAttribute(Core::Command::CA_UpdateText);
406 subversionMenu->addAction(command);
407 m_commandLocator->appendCommand(command);
409 m_commitProjectAction = new Utils::ParameterAction(tr("Commit Project"), tr("Commit Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this);
410 command = ami->registerAction(m_commitProjectAction, CMD_ID_COMMIT_PROJECT, globalcontext);
411 connect(m_commitProjectAction, SIGNAL(triggered()), this, SLOT(startCommitProject()));
412 command->setAttribute(Core::Command::CA_UpdateText);
413 subversionMenu->addAction(command);
414 m_commandLocator->appendCommand(command);
416 subversionMenu->addAction(createSeparator(this, ami, CMD_ID_SEPARATOR2, globalcontext));
418 m_diffRepositoryAction = new QAction(tr("Diff Repository"), this);
419 command = ami->registerAction(m_diffRepositoryAction, CMD_ID_REPOSITORYDIFF, globalcontext);
420 connect(m_diffRepositoryAction, SIGNAL(triggered()), this, SLOT(diffRepository()));
421 subversionMenu->addAction(command);
422 m_commandLocator->appendCommand(command);
424 m_statusRepositoryAction = new QAction(tr("Repository Status"), this);
425 command = ami->registerAction(m_statusRepositoryAction, CMD_ID_REPOSITORYSTATUS, globalcontext);
426 connect(m_statusRepositoryAction, SIGNAL(triggered()), this, SLOT(statusRepository()));
427 subversionMenu->addAction(command);
428 m_commandLocator->appendCommand(command);
430 m_logRepositoryAction = new QAction(tr("Log Repository"), this);
431 command = ami->registerAction(m_logRepositoryAction, CMD_ID_REPOSITORYLOG, globalcontext);
432 connect(m_logRepositoryAction, SIGNAL(triggered()), this, SLOT(logRepository()));
433 subversionMenu->addAction(command);
434 m_commandLocator->appendCommand(command);
436 m_updateRepositoryAction = new QAction(tr("Update Repository"), this);
437 command = ami->registerAction(m_updateRepositoryAction, CMD_ID_REPOSITORYUPDATE, globalcontext);
438 connect(m_updateRepositoryAction, SIGNAL(triggered()), this, SLOT(updateRepository()));
439 subversionMenu->addAction(command);
440 m_commandLocator->appendCommand(command);
442 m_commitAllAction = new QAction(tr("Commit All Files"), this);
443 command = ami->registerAction(m_commitAllAction, CMD_ID_COMMIT_ALL,
445 connect(m_commitAllAction, SIGNAL(triggered()), this, SLOT(startCommitAll()));
446 subversionMenu->addAction(command);
447 m_commandLocator->appendCommand(command);
449 m_describeAction = new QAction(tr("Describe..."), this);
450 command = ami->registerAction(m_describeAction, CMD_ID_DESCRIBE, globalcontext);
451 connect(m_describeAction, SIGNAL(triggered()), this, SLOT(slotDescribe()));
452 subversionMenu->addAction(command);
454 m_revertRepositoryAction = new QAction(tr("Revert Repository..."), this);
455 command = ami->registerAction(m_revertRepositoryAction, CMD_ID_REVERT_ALL,
457 connect(m_revertRepositoryAction, SIGNAL(triggered()), this, SLOT(revertAll()));
458 subversionMenu->addAction(command);
459 m_commandLocator->appendCommand(command);
461 // Actions of the submit editor
462 Core::Context svncommitcontext(Constants::SUBVERSIONCOMMITEDITOR);
464 m_submitCurrentLogAction = new QAction(VCSBase::VCSBaseSubmitEditor::submitIcon(), tr("Commit"), this);
465 command = ami->registerAction(m_submitCurrentLogAction, Constants::SUBMIT_CURRENT, svncommitcontext);
466 command->setAttribute(Core::Command::CA_UpdateText);
467 connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));
469 m_submitDiffAction = new QAction(VCSBase::VCSBaseSubmitEditor::diffIcon(), tr("Diff Selected Files"), this);
470 command = ami->registerAction(m_submitDiffAction , Constants::DIFF_SELECTED, svncommitcontext);
472 m_submitUndoAction = new QAction(tr("&Undo"), this);
473 command = ami->registerAction(m_submitUndoAction, Core::Constants::UNDO, svncommitcontext);
475 m_submitRedoAction = new QAction(tr("&Redo"), this);
476 command = ami->registerAction(m_submitRedoAction, Core::Constants::REDO, svncommitcontext);
481 bool SubversionPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
483 if (!isCommitEditorOpen())
486 Core::IFile *fileIFace = submitEditor->file();
487 const SubversionSubmitEditor *editor = qobject_cast<SubversionSubmitEditor *>(submitEditor);
488 if (!fileIFace || !editor)
491 // Submit editor closing. Make it write out the commit message
492 // and retrieve files
493 const QFileInfo editorFile(fileIFace->fileName());
494 const QFileInfo changeFile(m_commitMessageFileName);
495 if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
496 return true; // Oops?!
498 // Prompt user. Force a prompt unless submit was actually invoked (that
499 // is, the editor was closed or shutdown).
500 SubversionSettings newSettings = m_settings;
501 const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer =
502 editor->promptSubmit(tr("Closing Subversion Editor"),
503 tr("Do you want to commit the change?"),
504 tr("The commit message check failed. Do you want to commit the change?"),
505 &newSettings.promptToSubmit, !m_submitActionTriggered);
506 m_submitActionTriggered = false;
508 case VCSBase::VCSBaseSubmitEditor::SubmitCanceled:
509 return false; // Keep editing and change file
510 case VCSBase::VCSBaseSubmitEditor::SubmitDiscarded:
511 cleanCommitMessageFile();
512 return true; // Cancel all
516 setSettings(newSettings); // in case someone turned prompting off
517 const QStringList fileList = editor->checkedFiles();
518 bool closeEditor = true;
519 if (!fileList.empty()) {
520 // get message & commit
521 Core::ICore::instance()->fileManager()->blockFileChange(fileIFace);
523 Core::ICore::instance()->fileManager()->unblockFileChange(fileIFace);
524 closeEditor= commit(m_commitMessageFileName, fileList);
527 cleanCommitMessageFile();
531 void SubversionPlugin::diffCommitFiles(const QStringList &files)
533 svnDiff(m_commitRepository, files);
536 static inline void setDiffBaseDirectory(Core::IEditor *editor, const QString &db)
538 if (VCSBase::VCSBaseEditorWidget *ve = qobject_cast<VCSBase::VCSBaseEditorWidget*>(editor->widget()))
539 ve->setDiffBaseDirectory(db);
542 void SubversionPlugin::svnDiff(const QString &workingDir, const QStringList &files, QString diffname)
544 if (Subversion::Constants::debug)
545 qDebug() << Q_FUNC_INFO << files << diffname;
546 const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files);
547 QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditorWidget::getCodec(source);
549 if (files.count() == 1 && diffname.isEmpty())
550 diffname = QFileInfo(files.front()).fileName();
552 QStringList args(QLatin1String("diff"));
555 const SubversionResponse response =
556 runSvn(workingDir, args, m_settings.timeOutMS(), 0, codec);
560 // diff of a single file? re-use an existing view if possible to support
561 // the common usage pattern of continuously changing and diffing a file
562 if (files.count() == 1) {
563 // Show in the same editor if diff has been executed before
564 if (Core::IEditor *editor = locateEditor("originalFileName", files.front())) {
565 editor->createNew(response.stdOut);
566 Core::EditorManager::instance()->activateEditor(editor, Core::EditorManager::ModeSwitch);
567 setDiffBaseDirectory(editor, workingDir);
571 const QString title = QString::fromLatin1("svn diff %1").arg(diffname);
572 Core::IEditor *editor = showOutputInEditor(title, response.stdOut, VCSBase::DiffOutput, source, codec);
573 setDiffBaseDirectory(editor, workingDir);
574 if (files.count() == 1)
575 editor->setProperty("originalFileName", files.front());
578 SubversionSubmitEditor *SubversionPlugin::openSubversionSubmitEditor(const QString &fileName)
580 Core::IEditor *editor = Core::EditorManager::instance()->openEditor(fileName,
581 QLatin1String(Constants::SUBVERSIONCOMMITEDITOR_ID),
582 Core::EditorManager::ModeSwitch);
583 SubversionSubmitEditor *submitEditor = qobject_cast<SubversionSubmitEditor*>(editor);
584 QTC_ASSERT(submitEditor, /**/);
585 submitEditor->registerActions(m_submitUndoAction, m_submitRedoAction, m_submitCurrentLogAction, m_submitDiffAction);
586 connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffCommitFiles(QStringList)));
587 submitEditor->setCheckScriptWorkingDirectory(m_commitRepository);
591 void SubversionPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
593 if (!enableMenuAction(as, m_menuAction)) {
594 m_commandLocator->setEnabled(false);
597 const bool hasTopLevel = currentState().hasTopLevel();
598 m_commandLocator->setEnabled(hasTopLevel);
599 m_logRepositoryAction->setEnabled(hasTopLevel);
601 const QString projectName = currentState().currentProjectName();
602 m_diffProjectAction->setParameter(projectName);
603 m_statusProjectAction->setParameter(projectName);
604 m_updateProjectAction->setParameter(projectName);
605 m_logProjectAction->setParameter(projectName);
606 m_commitProjectAction->setParameter(projectName);
608 const bool repoEnabled = currentState().hasTopLevel();
609 m_commitAllAction->setEnabled(repoEnabled);
610 m_describeAction->setEnabled(repoEnabled);
611 m_revertRepositoryAction->setEnabled(repoEnabled);
612 m_diffRepositoryAction->setEnabled(repoEnabled);
613 m_statusRepositoryAction->setEnabled(repoEnabled);
614 m_updateRepositoryAction->setEnabled(repoEnabled);
616 const QString fileName = currentState().currentFileName();
618 m_addAction->setParameter(fileName);
619 m_deleteAction->setParameter(fileName);
620 m_revertAction->setParameter(fileName);
621 m_diffCurrentAction->setParameter(fileName);
622 m_commitCurrentAction->setParameter(fileName);
623 m_filelogCurrentAction->setParameter(fileName);
624 m_annotateCurrentAction->setParameter(fileName);
627 void SubversionPlugin::addCurrentFile()
629 const VCSBase::VCSBasePluginState state = currentState();
630 QTC_ASSERT(state.hasFile(), return)
631 vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
634 void SubversionPlugin::revertAll()
636 const VCSBase::VCSBasePluginState state = currentState();
637 QTC_ASSERT(state.hasTopLevel(), return)
638 const QString title = tr("Revert repository");
639 if (QMessageBox::warning(0, title, tr("Revert all pending changes to the repository?"),
640 QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
642 // NoteL: Svn "revert ." doesn not work.
644 args << QLatin1String("revert") << QLatin1String("--recursive") << state.topLevel();
645 const SubversionResponse revertResponse =
646 runSvn(state.topLevel(), args, m_settings.timeOutMS(),
647 SshPasswordPrompt|ShowStdOutInLogWindow);
648 if (revertResponse.error) {
649 QMessageBox::warning(0, title, tr("Revert failed: %1").arg(revertResponse.message), QMessageBox::Ok);
651 subVersionControl()->emitRepositoryChanged(state.topLevel());
655 void SubversionPlugin::revertCurrentFile()
657 const VCSBase::VCSBasePluginState state = currentState();
658 QTC_ASSERT(state.hasFile(), return)
660 QStringList args(QLatin1String("diff"));
661 args.push_back(state.relativeCurrentFile());
663 const SubversionResponse diffResponse =
664 runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(), 0);
665 if (diffResponse.error)
668 if (diffResponse.stdOut.isEmpty())
670 if (QMessageBox::warning(0, QLatin1String("svn revert"), tr("The file has been changed. Do you want to revert it?"),
671 QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
675 Core::FileChangeBlocker fcb(state.currentFile());
679 args << QLatin1String("revert") << state.relativeCurrentFile();
681 const SubversionResponse revertResponse =
682 runSvn(state.currentFileTopLevel(), args, m_settings.timeOutMS(),
683 SshPasswordPrompt|ShowStdOutInLogWindow);
685 if (!revertResponse.error) {
686 subVersionControl()->emitFilesChanged(QStringList(state.currentFile()));
690 void SubversionPlugin::diffProject()
692 const VCSBase::VCSBasePluginState state = currentState();
693 QTC_ASSERT(state.hasProject(), return)
694 svnDiff(state.currentProjectTopLevel(), state.relativeCurrentProject(), state.currentProjectName());
697 void SubversionPlugin::diffCurrentFile()
699 const VCSBase::VCSBasePluginState state = currentState();
700 QTC_ASSERT(state.hasFile(), return)
701 svnDiff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
704 void SubversionPlugin::startCommitCurrentFile()
706 const VCSBase::VCSBasePluginState state = currentState();
707 QTC_ASSERT(state.hasFile(), return)
708 startCommit(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
711 void SubversionPlugin::startCommitAll()
713 const VCSBase::VCSBasePluginState state = currentState();
714 QTC_ASSERT(state.hasTopLevel(), return);
715 startCommit(state.topLevel());
718 void SubversionPlugin::startCommitProject()
720 const VCSBase::VCSBasePluginState state = currentState();
721 QTC_ASSERT(state.hasProject(), return);
722 startCommit(state.currentProjectPath());
725 /* Start commit of files of a single repository by displaying
726 * template and files in a submit editor. On closing, the real
727 * commit will start. */
728 void SubversionPlugin::startCommit(const QString &workingDir, const QStringList &files)
730 if (VCSBase::VCSBaseSubmitEditor::raiseSubmitEditor())
732 if (isCommitEditorOpen()) {
733 VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("Another commit is currently being executed."));
737 QStringList args(QLatin1String("status"));
740 const SubversionResponse response =
741 runSvn(workingDir, args, m_settings.timeOutMS(), 0);
745 // Get list of added/modified/deleted files
746 const StatusList statusOutput = parseStatusOutput(response.stdOut);
747 if (statusOutput.empty()) {
748 VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("There are no modified files."));
751 m_commitRepository = workingDir;
752 // Create a new submit change file containing the submit template
753 QTemporaryFile changeTmpFile;
754 changeTmpFile.setAutoRemove(false);
755 if (!changeTmpFile.open()) {
756 VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Cannot create temporary file: %1").arg(changeTmpFile.errorString()));
759 m_commitMessageFileName = changeTmpFile.fileName();
760 // TODO: Regitctrieve submit template from
761 const QString submitTemplate;
763 changeTmpFile.write(submitTemplate.toUtf8());
764 changeTmpFile.flush();
765 changeTmpFile.close();
766 // Create a submit editor and set file list
767 SubversionSubmitEditor *editor = openSubversionSubmitEditor(m_commitMessageFileName);
768 editor->setStatusList(statusOutput);
771 bool SubversionPlugin::commit(const QString &messageFile,
772 const QStringList &subVersionFileList)
774 if (Subversion::Constants::debug)
775 qDebug() << Q_FUNC_INFO << messageFile << subVersionFileList;
776 // Transform the status list which is sth
777 // "[ADM]<blanks>file" into an args list. The files of the status log
778 // can be relative or absolute depending on where the command was run.
779 QStringList args = QStringList(QLatin1String("commit"));
780 args << QLatin1String(nonInteractiveOptionC) << QLatin1String("--file") << messageFile;
781 args.append(subVersionFileList);
782 const SubversionResponse response =
783 runSvn(m_commitRepository, args, m_settings.longTimeOutMS(),
784 SshPasswordPrompt|ShowStdOutInLogWindow);
785 return !response.error ;
788 void SubversionPlugin::filelogCurrentFile()
790 const VCSBase::VCSBasePluginState state = currentState();
791 QTC_ASSERT(state.hasFile(), return)
792 filelog(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true);
795 void SubversionPlugin::logProject()
797 const VCSBase::VCSBasePluginState state = currentState();
798 QTC_ASSERT(state.hasProject(), return)
799 filelog(state.currentProjectTopLevel(), state.relativeCurrentProject());
802 void SubversionPlugin::logRepository()
804 const VCSBase::VCSBasePluginState state = currentState();
805 QTC_ASSERT(state.hasTopLevel(), return)
806 filelog(state.topLevel());
809 void SubversionPlugin::diffRepository()
811 const VCSBase::VCSBasePluginState state = currentState();
812 QTC_ASSERT(state.hasTopLevel(), return)
813 svnDiff(state.topLevel(), QStringList());
816 void SubversionPlugin::statusRepository()
818 const VCSBase::VCSBasePluginState state = currentState();
819 QTC_ASSERT(state.hasTopLevel(), return)
820 svnStatus(state.topLevel());
823 void SubversionPlugin::updateRepository()
825 const VCSBase::VCSBasePluginState state = currentState();
826 QTC_ASSERT(state.hasTopLevel(), return)
827 svnUpdate(state.topLevel());
830 void SubversionPlugin::svnStatus(const QString &workingDir, const QStringList &relativePaths)
832 const VCSBase::VCSBasePluginState state = currentState();
833 QTC_ASSERT(state.hasTopLevel(), return)
834 QStringList args(QLatin1String("status"));
835 if (!relativePaths.isEmpty())
836 args.append(relativePaths);
837 VCSBase::VCSBaseOutputWindow *outwin = VCSBase::VCSBaseOutputWindow::instance();
838 outwin->setRepository(workingDir);
839 runSvn(workingDir, args, m_settings.timeOutMS(),
840 ShowStdOutInLogWindow|ShowSuccessMessage);
841 outwin->clearRepository();
844 void SubversionPlugin::filelog(const QString &workingDir,
845 const QStringList &files,
846 bool enableAnnotationContextMenu)
848 // no need for temp file
849 QStringList args(QLatin1String("log"));
850 if (m_settings.logCount > 0)
851 args << QLatin1String("-l") << QString::number(m_settings.logCount);
852 foreach(const QString &file, files)
853 args.append(QDir::toNativeSeparators(file));
855 // subversion stores log in UTF-8 and returns it back in user system locale.
856 // So we do not need to encode it.
857 const SubversionResponse response =
858 runSvn(workingDir, args, m_settings.timeOutMS(),
859 SshPasswordPrompt, 0/*codec*/);
863 // Re-use an existing view if possible to support
864 // the common usage pattern of continuously changing and diffing a file
866 const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, files);
867 if (Core::IEditor *editor = locateEditor("logFileName", id)) {
868 editor->createNew(response.stdOut);
869 Core::EditorManager::instance()->activateEditor(editor, Core::EditorManager::ModeSwitch);
871 const QString title = QString::fromLatin1("svn log %1").arg(id);
872 const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files);
873 Core::IEditor *newEditor = showOutputInEditor(title, response.stdOut, VCSBase::LogOutput, source, /*codec*/0);
874 newEditor->setProperty("logFileName", id);
875 if (enableAnnotationContextMenu)
876 VCSBase::VCSBaseEditorWidget::getVcsBaseEditor(newEditor)->setFileLogAnnotateEnabled(true);
880 void SubversionPlugin::updateProject()
882 const VCSBase::VCSBasePluginState state = currentState();
883 QTC_ASSERT(state.hasProject(), return);
884 svnUpdate(state.currentProjectTopLevel(), state.relativeCurrentProject());
887 void SubversionPlugin::svnUpdate(const QString &workingDir, const QStringList &relativePaths)
889 QStringList args(QLatin1String("update"));
890 args.push_back(QLatin1String(nonInteractiveOptionC));
891 if (!relativePaths.isEmpty())
892 args.append(relativePaths);
893 const SubversionResponse response =
894 runSvn(workingDir, args, m_settings.longTimeOutMS(),
895 SshPasswordPrompt|ShowStdOutInLogWindow);
897 subVersionControl()->emitRepositoryChanged(workingDir);
900 void SubversionPlugin::annotateCurrentFile()
902 const VCSBase::VCSBasePluginState state = currentState();
903 QTC_ASSERT(state.hasFile(), return);
904 vcsAnnotate(state.currentFileTopLevel(), state.relativeCurrentFile());
907 void SubversionPlugin::annotateVersion(const QString &file,
908 const QString &revision,
911 const QFileInfo fi(file);
912 vcsAnnotate(fi.absolutePath(), fi.fileName(), revision, lineNr);
915 void SubversionPlugin::vcsAnnotate(const QString &workingDir, const QString &file,
916 const QString &revision /* = QString() */,
917 int lineNumber /* = -1 */)
919 const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, file);
920 QTextCodec *codec = VCSBase::VCSBaseEditorWidget::getCodec(source);
922 QStringList args(QLatin1String("annotate"));
923 if (m_settings.spaceIgnorantAnnotation)
924 args << QLatin1String("-x") << QLatin1String("-uw");
925 if (!revision.isEmpty())
926 args << QLatin1String("-r") << revision;
927 args.push_back(QLatin1String("-v"));
928 args.append(QDir::toNativeSeparators(file));
930 const SubversionResponse response =
931 runSvn(workingDir, args, m_settings.timeOutMS(),
932 SshPasswordPrompt|ForceCLocale, codec);
936 // Re-use an existing view if possible to support
937 // the common usage pattern of continuously changing and diffing a file
939 lineNumber = VCSBase::VCSBaseEditorWidget::lineNumberOfCurrentEditor(source);
941 const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, QStringList(file), revision);
943 if (Core::IEditor *editor = locateEditor("annotateFileName", id)) {
944 editor->createNew(response.stdOut);
945 VCSBase::VCSBaseEditorWidget::gotoLineOfEditor(editor, lineNumber);
946 Core::EditorManager::instance()->activateEditor(editor, Core::EditorManager::ModeSwitch);
948 const QString title = QString::fromLatin1("svn annotate %1").arg(id);
949 Core::IEditor *newEditor = showOutputInEditor(title, response.stdOut, VCSBase::AnnotateOutput, source, codec);
950 newEditor->setProperty("annotateFileName", id);
951 VCSBase::VCSBaseEditorWidget::gotoLineOfEditor(newEditor, lineNumber);
955 void SubversionPlugin::projectStatus()
957 const VCSBase::VCSBasePluginState state = currentState();
958 QTC_ASSERT(state.hasProject(), return);
959 svnStatus(state.currentFileTopLevel(), state.relativeCurrentProject());
962 void SubversionPlugin::describe(const QString &source, const QString &changeNr)
964 // To describe a complete change, find the top level and then do
965 //svn diff -r 472958:472959 <top level>
966 const QFileInfo fi(source);
968 const bool manages = managesDirectory(fi.isDir() ? source : fi.absolutePath(), &topLevel);
969 if (!manages || topLevel.isEmpty())
971 if (Subversion::Constants::debug)
972 qDebug() << Q_FUNC_INFO << source << topLevel << changeNr;
973 // Number must be > 1
975 const int number = changeNr.toInt(&ok);
976 if (!ok || number < 2)
978 // Run log to obtain message (local utf8)
980 QStringList args(QLatin1String("log"));
981 args.push_back(QLatin1String("-r"));
982 args.push_back(changeNr);
983 const SubversionResponse logResponse =
984 runSvn(topLevel, args, m_settings.timeOutMS(), SshPasswordPrompt);
985 if (logResponse.error)
987 description = logResponse.stdOut;
989 // Run diff (encoding via source codec)
991 args.push_back(QLatin1String("diff"));
992 args.push_back(QLatin1String("-r"));
994 QTextStream(&diffArg) << (number - 1) << ':' << number;
995 args.push_back(diffArg);
997 QTextCodec *codec = VCSBase::VCSBaseEditorWidget::getCodec(source);
998 const SubversionResponse response =
999 runSvn(topLevel, args, m_settings.timeOutMS(),
1000 SshPasswordPrompt, codec);
1003 description += response.stdOut;
1005 // Re-use an existing view if possible to support
1006 // the common usage pattern of continuously changing and diffing a file
1007 const QString id = diffArg + source;
1008 if (Core::IEditor *editor = locateEditor("describeChange", id)) {
1009 editor->createNew(description);
1010 Core::EditorManager::instance()->activateEditor(editor, Core::EditorManager::ModeSwitch);
1012 const QString title = QString::fromLatin1("svn describe %1#%2").arg(fi.fileName(), changeNr);
1013 Core::IEditor *newEditor = showOutputInEditor(title, description, VCSBase::DiffOutput, source, codec);
1014 newEditor->setProperty("describeChange", id);
1018 void SubversionPlugin::slotDescribe()
1020 const VCSBase::VCSBasePluginState state = currentState();
1021 QTC_ASSERT(state.hasTopLevel(), return);
1023 QInputDialog inputDialog(Core::ICore::instance()->mainWindow());
1024 inputDialog.setWindowFlags(inputDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
1025 inputDialog.setInputMode(QInputDialog::IntInput);
1026 inputDialog.setIntRange(2, INT_MAX);
1027 inputDialog.setWindowTitle(tr("Describe"));
1028 inputDialog.setLabelText(tr("Revision number:"));
1029 if (inputDialog.exec() != QDialog::Accepted)
1032 const int revision = inputDialog.intValue();
1033 describe(state.topLevel(), QString::number(revision));
1036 void SubversionPlugin::submitCurrentLog()
1038 m_submitActionTriggered = true;
1039 Core::EditorManager::instance()->closeEditors(QList<Core::IEditor*>()
1040 << Core::EditorManager::instance()->currentEditor());
1044 SubversionPlugin::runSvn(const QString &workingDir,
1045 const QStringList &arguments,
1048 QTextCodec *outputCodec)
1051 return m_settings.hasAuthentication() ?
1052 runSvn(workingDir, m_settings.user, m_settings.password,
1053 arguments, timeOut, flags, outputCodec) :
1054 runSvn(workingDir, QString(), QString(),
1055 arguments, timeOut, flags, outputCodec);
1058 // Add authorization options to the command line arguments.
1059 // SVN pre 1.5 does not accept "--userName" for "add", which is most likely
1060 // an oversight. As no password is needed for the option, generally omit it.
1061 QStringList SubversionPlugin::addAuthenticationOptions(const QStringList &args,
1062 const QString &userName, const QString &password)
1064 if (userName.isEmpty())
1066 if (!args.empty() && args.front() == QLatin1String("add"))
1069 rc.push_back(QLatin1String("--username"));
1070 rc.push_back(userName);
1071 if (!password.isEmpty()) {
1072 rc.push_back(QLatin1String("--password"));
1073 rc.push_back(password);
1079 SubversionResponse SubversionPlugin::runSvn(const QString &workingDir,
1080 const QString &userName, const QString &password,
1081 const QStringList &arguments, int timeOut,
1082 unsigned flags, QTextCodec *outputCodec)
1084 const QString executable = m_settings.svnCommand;
1085 SubversionResponse response;
1086 if (executable.isEmpty()) {
1087 response.error = true;
1088 response.message =tr("No subversion executable specified!");
1092 const QStringList completeArguments = SubversionPlugin::addAuthenticationOptions(arguments, userName, password);
1093 const Utils::SynchronousProcessResponse sp_resp =
1094 VCSBase::VCSBasePlugin::runVCS(workingDir, executable,
1095 completeArguments, timeOut, flags, outputCodec);
1097 response.error = sp_resp.result != Utils::SynchronousProcessResponse::Finished;
1099 response.message = sp_resp.exitMessage(executable, timeOut);
1100 response.stdErr = sp_resp.stdErr;
1101 response.stdOut = sp_resp.stdOut;
1105 Core::IEditor * SubversionPlugin::showOutputInEditor(const QString& title, const QString &output,
1106 int editorType, const QString &source,
1109 const VCSBase::VCSBaseEditorParameters *params = findType(editorType);
1110 QTC_ASSERT(params, return 0);
1111 const QString id = params->id;
1112 if (Subversion::Constants::debug)
1113 qDebug() << "SubversionPlugin::showOutputInEditor" << title << id << "Size= " << output.size() << " Type=" << editorType << debugCodec(codec);
1115 Core::IEditor *editor = Core::EditorManager::instance()->openEditorWithContents(id, &s, output);
1116 connect(editor, SIGNAL(annotateRevisionRequested(QString,QString,int)),
1117 this, SLOT(annotateVersion(QString,QString,int)));
1118 SubversionEditor *e = qobject_cast<SubversionEditor*>(editor->widget());
1121 e->setForceReadOnly(true);
1122 s.replace(QLatin1Char(' '), QLatin1Char('_'));
1123 e->setSuggestedFileName(s);
1124 if (!source.isEmpty())
1125 e->setSource(source);
1128 Core::IEditor *ie = e->editor();
1129 Core::EditorManager::instance()->activateEditor(ie, Core::EditorManager::ModeSwitch);
1133 SubversionSettings SubversionPlugin::settings() const
1138 void SubversionPlugin::setSettings(const SubversionSettings &s)
1140 if (s != m_settings) {
1142 if (QSettings *settings = Core::ICore::instance()->settings())
1143 m_settings.toSettings(settings);
1147 SubversionPlugin *SubversionPlugin::subversionPluginInstance()
1149 QTC_ASSERT(m_subversionPluginInstance, return m_subversionPluginInstance);
1150 return m_subversionPluginInstance;
1153 bool SubversionPlugin::vcsAdd(const QString &workingDir, const QString &rawFileName)
1155 #ifdef Q_OS_MAC // See below.
1156 return vcsAdd14(workingDir, rawFileName);
1158 return vcsAdd15(workingDir, rawFileName);
1162 // Post 1.4 add: Use "--parents" to add directories
1163 bool SubversionPlugin::vcsAdd15(const QString &workingDir, const QString &rawFileName)
1165 const QString file = QDir::toNativeSeparators(rawFileName);
1167 args << QLatin1String("add") << QLatin1String("--parents") << file;
1168 const SubversionResponse response =
1169 runSvn(workingDir, args, m_settings.timeOutMS(),
1170 SshPasswordPrompt|ShowStdOutInLogWindow);
1171 return !response.error;
1174 // Pre 1.5 add: Add directories in a loop. To be deprecated
1175 // once Mac ships newer svn-versions
1176 bool SubversionPlugin::vcsAdd14(const QString &workingDir, const QString &rawFileName)
1178 const QChar slash = QLatin1Char('/');
1179 const QStringList relativePath = rawFileName.split(slash);
1180 // Add directories (dir1/dir2/file.cpp) in a loop.
1181 if (relativePath.size() > 1) {
1183 const int lastDir = relativePath.size() - 1;
1184 for (int p = 0; p < lastDir; p++) {
1185 if (!path.isEmpty())
1187 path += relativePath.at(p);
1188 if (!checkSVNSubDir(QDir(path))) {
1189 QStringList addDirArgs;
1190 addDirArgs << QLatin1String("add") << QLatin1String("--non-recursive") << QDir::toNativeSeparators(path);
1191 const SubversionResponse addDirResponse =
1192 runSvn(workingDir, addDirArgs, m_settings.timeOutMS(),
1193 SshPasswordPrompt|ShowStdOutInLogWindow);
1194 if (addDirResponse.error)
1201 args << QLatin1String("add") << QDir::toNativeSeparators(rawFileName);
1202 const SubversionResponse response =
1203 runSvn(workingDir, args, m_settings.timeOutMS(),
1204 SshPasswordPrompt|ShowStdOutInLogWindow);
1205 return !response.error;
1208 bool SubversionPlugin::vcsDelete(const QString &workingDir, const QString &rawFileName)
1210 const QString file = QDir::toNativeSeparators(rawFileName);
1212 QStringList args(QLatin1String("delete"));
1213 args.push_back(file);
1215 const SubversionResponse response =
1216 runSvn(workingDir, args, m_settings.timeOutMS(),
1217 SshPasswordPrompt|ShowStdOutInLogWindow);
1218 return !response.error;
1221 bool SubversionPlugin::vcsMove(const QString &workingDir, const QString &from, const QString &to)
1223 QStringList args(QLatin1String("move"));
1224 args << QDir::toNativeSeparators(from) << QDir::toNativeSeparators(to);
1225 const SubversionResponse response =
1226 runSvn(workingDir, args, m_settings.timeOutMS(),
1227 SshPasswordPrompt|ShowStdOutInLogWindow|FullySynchronously);
1228 return !response.error;
1231 bool SubversionPlugin::vcsCheckout(const QString &directory, const QByteArray &url)
1234 tempUrl.setEncodedUrl(url);
1235 QString username = tempUrl.userName();
1236 QString password = tempUrl.password();
1237 QStringList args = QStringList(QLatin1String("checkout"));
1238 args << QLatin1String(nonInteractiveOptionC) ;
1240 if(!username.isEmpty() && !password.isEmpty())
1242 // If url contains username and password we have to use separate username and password
1243 // arguments instead of passing those in the url. Otherwise the subversion 'non-interactive'
1244 // authentication will always fail (if the username and password data are not stored locally),
1245 // if for example we are logging into a new host for the first time using svn. There seems to
1246 // be a bug in subversion, so this might get fixed in the future.
1247 tempUrl.setUserInfo("");
1248 args << tempUrl.toEncoded() << directory;
1249 const SubversionResponse response = runSvn(directory, username, password, args,
1250 m_settings.longTimeOutMS(),
1251 VCSBase::VCSBasePlugin::SshPasswordPrompt);
1252 return !response.error;
1254 args << url << directory;
1255 const SubversionResponse response = runSvn(directory, args, m_settings.longTimeOutMS(),
1256 VCSBase::VCSBasePlugin::SshPasswordPrompt);
1257 return !response.error;
1261 QString SubversionPlugin::vcsGetRepositoryURL(const QString &directory)
1263 QXmlStreamReader xml;
1264 QStringList args = QStringList(QLatin1String("info"));
1265 args << QLatin1String("--xml");
1267 const SubversionResponse response = runSvn(directory, args, m_settings.longTimeOutMS(), SuppressCommandLogging);
1268 xml.addData(response.stdOut);
1273 while(!xml.atEnd() && !xml.hasError()) {
1274 switch(xml.readNext()) {
1275 case QXmlStreamReader::StartDocument:
1277 case QXmlStreamReader::StartElement:
1278 if(xml.name() == QLatin1String("repository"))
1280 else if(repo && xml.name() == QLatin1String("root"))
1283 case QXmlStreamReader::EndElement:
1284 if(xml.name() == QLatin1String("repository"))
1286 else if(repo && xml.name() == QLatin1String("root"))
1289 case QXmlStreamReader::Characters:
1291 return xml.text().toString();
1300 /* Subversion has ".svn" directory in each directory
1301 * it manages. The top level is the first directory
1302 * under the directory that does not have a ".svn". */
1303 bool SubversionPlugin::managesDirectory(const QString &directory, QString *topLevel /* = 0 */) const
1305 const QDir dir(directory);
1308 bool manages = false;
1310 if (!dir.exists() || !checkSVNSubDir(dir))
1315 /* Recursing up, the top level is a child of the first directory that does
1316 * not have a ".svn" directory. The starting directory must be a managed
1317 * one. Go up and try to find the first unmanaged parent dir. */
1318 QDir lastDirectory = dir;
1319 for (QDir parentDir = lastDirectory; parentDir.cdUp() ; lastDirectory = parentDir) {
1320 if (!checkSVNSubDir(parentDir)) {
1321 *topLevel = lastDirectory.absolutePath();
1326 if (Subversion::Constants::debug) {
1327 QDebug nsp = qDebug().nospace();
1328 nsp << "SubversionPlugin::managesDirectory" << directory << manages;
1335 // Check whether SVN management subdirs exist.
1336 bool SubversionPlugin::checkSVNSubDir(const QDir &directory) const
1338 const int dirCount = m_svnDirectories.size();
1339 for (int i = 0; i < dirCount; i++) {
1340 const QString svnDir = directory.absoluteFilePath(m_svnDirectories.at(i));
1341 if (QFileInfo(svnDir).isDir())
1347 SubversionControl *SubversionPlugin::subVersionControl() const
1349 return static_cast<SubversionControl *>(versionControl());
1352 Q_EXPORT_PLUGIN(SubversionPlugin)