File indexing completed on 2024-05-12 17:16:16
0001 /*************************************************************************** 0002 * Copyright (C) 2005-2009 by Rajko Albrecht * 0003 * ral@alwins-world.de * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 0019 ***************************************************************************/ 0020 #include "svnactions.h" 0021 #include "checkoutinfo_impl.h" 0022 #include "itemdisplay.h" 0023 #include "svnitem.h" 0024 #include "rangeinput_impl.h" 0025 #include "propertiesdlg.h" 0026 #include "ccontextlistener.h" 0027 #include "tcontextlistener.h" 0028 #include "modifiedthread.h" 0029 #include "fillcachethread.h" 0030 #include "svnlogdlgimp.h" 0031 #include "stopdlg.h" 0032 #include "blamedisplay.h" 0033 #include "ksvnwidgets/commitmsg_impl.h" 0034 #include "ksvnwidgets/models/commitmodelhelper.h" 0035 #include "ksvnwidgets/diffbrowser.h" 0036 #include "ksvnwidgets/encodingselector_impl.h" 0037 #include "ksvnwidgets/revertform.h" 0038 #include "graphtree/revisiontree.h" 0039 #include "graphtree/revtreewidget.h" 0040 #include "settings/kdesvnsettings.h" 0041 #include "svnqt/client.h" 0042 #include "svnqt/annotate_line.h" 0043 #include "svnqt/context_listener.h" 0044 #include "svnqt/dirent.h" 0045 #include "svnqt/targets.h" 0046 #include "svnqt/url.h" 0047 #include "svnqt/svnqttypes.h" 0048 #include "svnqt/svnqt_defines.h" 0049 #include "svnqt/client_parameter.h" 0050 #include "svnqt/client_commit_parameter.h" 0051 #include "svnqt/client_annotate_parameter.h" 0052 #include "svnqt/client_update_parameter.h" 0053 #include "svnqt/cache/LogCache.h" 0054 #include "svnqt/cache/ReposLog.h" 0055 #include "svnqt/cache/ReposConfig.h" 0056 0057 #include "fronthelpers/watchedprocess.h" 0058 0059 #include "helpers/stringhelper.h" 0060 #include "helpers/kdesvn_debug.h" 0061 #include "helpers/ktranslateurl.h" 0062 #include "helpers/windowgeometryhelper.h" 0063 #include "fronthelpers/cursorstack.h" 0064 #include "cacheentry.h" 0065 0066 #include <klocalizedstring.h> 0067 #include <kmessagebox.h> 0068 #include <kconfig.h> 0069 #include <kmimetypetrader.h> 0070 #include <krun.h> 0071 0072 #include <QApplication> 0073 #include <QDesktopServices> 0074 #include <QFileInfo> 0075 #include <QFontDatabase> 0076 #include <QInputDialog> 0077 #include <QMap> 0078 #include <QMimeDatabase> 0079 #include <QReadWriteLock> 0080 #include <QString> 0081 #include <QTimer> 0082 #include <QTemporaryDir> 0083 #include <QTemporaryFile> 0084 #include <QTreeWidget> 0085 #include <QTreeWidgetItem> 0086 0087 #include <sys/time.h> 0088 #include <unistd.h> 0089 0090 /// @todo has to be removed for a real fix of ticket #613 0091 #include <svn_config.h> 0092 // wait not longer than 10 seconds for a thread 0093 #define MAX_THREAD_WAITTIME 10000 0094 0095 class SvnActionsData 0096 { 0097 public: 0098 SvnActionsData() 0099 : m_ParentList(nullptr) 0100 , m_SvnContextListener(nullptr) 0101 , m_Svnclient(svn::Client::getobject(svn::ContextP())) 0102 , runblocked(false) 0103 { 0104 } 0105 0106 ~SvnActionsData() 0107 { 0108 cleanDialogs(); 0109 delete m_SvnContextListener; 0110 } 0111 0112 static bool isExternalDiff() 0113 { 0114 if (Kdesvnsettings::use_external_diff()) { 0115 const QString edisp = Kdesvnsettings::external_diff_display(); 0116 const QVector<QStringRef> wlist = edisp.splitRef(QLatin1Char(' ')); 0117 if (wlist.count() >= 3 && edisp.contains(QLatin1String("%1")) && edisp.contains(QLatin1String("%2"))) { 0118 return true; 0119 } 0120 } 0121 return false; 0122 } 0123 0124 void clearCaches() 0125 { 0126 QWriteLocker wl(&(m_InfoCacheLock)); 0127 m_PropertiesCache.clear(); 0128 m_contextData.clear(); 0129 m_InfoCache.clear(); 0130 } 0131 0132 void cleanDialogs() 0133 { 0134 if (m_DiffDialog) { 0135 delete m_DiffDialog; 0136 m_DiffDialog = nullptr; 0137 } 0138 if (m_LogDialog) { 0139 m_LogDialog->saveSize(); 0140 delete m_LogDialog; 0141 m_LogDialog = nullptr; 0142 } 0143 } 0144 0145 /// @todo set some standards options to svn::Context. This should made via a Config class in svnqt (future release 1.4) 0146 /// this is a workaround for ticket #613 0147 void setStandards() 0148 { 0149 if (!m_CurrentContext) { 0150 return; 0151 } 0152 svn_config_t *cfg_config = static_cast<svn_config_t *>(apr_hash_get(m_CurrentContext->ctx()->config, SVN_CONFIG_CATEGORY_CONFIG, 0153 APR_HASH_KEY_STRING)); 0154 if (!cfg_config) { 0155 return; 0156 } 0157 svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS, 0158 SVN_CONFIG_OPTION_DIFF_CMD, nullptr); 0159 } 0160 0161 ItemDisplay *m_ParentList; 0162 0163 CContextListener *m_SvnContextListener; 0164 svn::ContextP m_CurrentContext; 0165 svn::ClientP m_Svnclient; 0166 0167 helpers::statusCache m_UpdateCache; 0168 helpers::statusCache m_Cache; 0169 helpers::statusCache m_conflictCache; 0170 helpers::statusCache m_repoLockCache; 0171 helpers::itemCache<svn::PathPropertiesMapListPtr> m_PropertiesCache; 0172 /// \todo as persistent cache (sqlite?) 0173 helpers::itemCache<svn::InfoEntry> m_InfoCache; 0174 helpers::itemCache<QVariant> m_MergeInfoCache; 0175 0176 QPointer<DiffBrowser> m_DiffBrowserPtr; 0177 QPointer<KSvnSimpleOkDialog> m_DiffDialog; 0178 QPointer<SvnLogDlgImp> m_LogDialog; 0179 0180 QMap<QString, QString> m_contextData; 0181 QReadWriteLock m_InfoCacheLock; 0182 0183 bool runblocked; 0184 }; 0185 0186 #define EMIT_FINISHED emit sendNotify(i18n("Finished")) 0187 #define EMIT_REFRESH emit sigRefreshAll() 0188 #define DIALOGS_SIZES "display_dialogs_sizes" 0189 0190 SvnActions::SvnActions(ItemDisplay *parent, bool processes_blocked) 0191 : QObject(parent ? parent->realWidget() : nullptr) 0192 , SimpleLogCb() 0193 , m_CThread(nullptr) 0194 , m_UThread(nullptr) 0195 , m_FCThread(nullptr) 0196 { 0197 m_Data.reset(new SvnActionsData); 0198 m_Data->m_ParentList = parent; 0199 m_Data->m_SvnContextListener = new CContextListener(this); 0200 m_Data->runblocked = processes_blocked; 0201 connect(m_Data->m_SvnContextListener, &CContextListener::sendNotify, 0202 this, &SvnActions::slotNotifyMessage); 0203 } 0204 0205 svn::ClientP SvnActions::svnclient() 0206 { 0207 return m_Data->m_Svnclient; 0208 } 0209 0210 SvnActions::~SvnActions() 0211 { 0212 killallThreads(); 0213 } 0214 0215 void SvnActions::slotNotifyMessage(const QString &aMsg) 0216 { 0217 emit sendNotify(aMsg); 0218 } 0219 0220 void SvnActions::reInitClient() 0221 { 0222 m_Data->clearCaches(); 0223 m_Data->cleanDialogs(); 0224 if (m_Data->m_CurrentContext) { 0225 m_Data->m_CurrentContext->setListener(nullptr); 0226 } 0227 m_Data->m_CurrentContext = svn::ContextP(new svn::Context); 0228 m_Data->m_CurrentContext->setListener(m_Data->m_SvnContextListener); 0229 m_Data->m_Svnclient->setContext(m_Data->m_CurrentContext); 0230 ///@todo workaround has to be replaced 0231 m_Data->setStandards(); 0232 } 0233 0234 void SvnActions::makeLog(const svn::Revision &start, const svn::Revision &end, const svn::Revision &peg, const QString &which, bool follow, bool list_files, int limit) 0235 { 0236 svn::LogEntriesMapPtr logs = getLog(start, end, peg, which, list_files, limit, follow); 0237 if (!logs) { 0238 return; 0239 } 0240 svn::InfoEntry info; 0241 if (!singleInfo(which, peg, info)) { 0242 return; 0243 } 0244 const QString reposRoot = info.reposRoot().toString(); 0245 bool need_modal = m_Data->runblocked || QApplication::activeModalWidget() != nullptr; 0246 if (need_modal || !m_Data->m_LogDialog) { 0247 m_Data->m_LogDialog = new SvnLogDlgImp(this, need_modal); 0248 connect(m_Data->m_LogDialog, &SvnLogDlgImp::makeDiff, 0249 this, QOverload<const QString&, const svn::Revision&, const QString&, const svn::Revision&, QWidget*>::of(&SvnActions::makeDiff)); 0250 connect(m_Data->m_LogDialog, &SvnLogDlgImp::makeCat, 0251 this, &SvnActions::slotMakeCat); 0252 } 0253 0254 if (m_Data->m_LogDialog) { 0255 m_Data->m_LogDialog->dispLog(logs, info.url().toString().mid(reposRoot.length()), reposRoot, 0256 ( 0257 peg == svn::Revision::UNDEFINED ? 0258 (svn::Url::isValid(which) ? svn::Revision::HEAD : svn::Revision::UNDEFINED) : 0259 peg 0260 ), which); 0261 if (need_modal) { 0262 m_Data->m_LogDialog->exec(); 0263 m_Data->m_LogDialog->saveSize(); 0264 delete m_Data->m_LogDialog; 0265 } else { 0266 m_Data->m_LogDialog->show(); 0267 m_Data->m_LogDialog->raise(); 0268 } 0269 } 0270 EMIT_FINISHED; 0271 } 0272 0273 svn::LogEntriesMapPtr SvnActions::getLog(const svn::Revision &start, const svn::Revision &end, const svn::Revision &peg, const QString &which, bool list_files, 0274 int limit, QWidget *parent) 0275 { 0276 return getLog(start, end, peg, which, list_files, limit, Kdesvnsettings::log_follows_nodes(), parent); 0277 } 0278 0279 svn::LogEntriesMapPtr SvnActions::getLog(const svn::Revision &start, const svn::Revision &end, const svn::Revision &peg, const QString &which, bool list_files, 0280 int limit, bool follow, QWidget *parent) 0281 { 0282 svn::LogEntriesMapPtr logs; 0283 if (!m_Data->m_CurrentContext) { 0284 return logs; 0285 } 0286 0287 bool mergeinfo = hasMergeInfo(m_Data->m_ParentList->baseUri().isEmpty() ? which : m_Data->m_ParentList->baseUri()); 0288 0289 svn::LogParameter params; 0290 params.targets(which).revisionRange(start, end).peg(peg).includeMergedRevisions(mergeinfo).limit(limit).discoverChangedPathes(list_files).strictNodeHistory(!follow); 0291 0292 try { 0293 StopDlg sdlg(m_Data->m_SvnContextListener, (parent ? parent : m_Data->m_ParentList->realWidget()), 0294 i18nc("@title:window", "Logs"), i18n("Getting logs - hit Cancel for abort")); 0295 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 0296 logs = svn::LogEntriesMapPtr(new svn::LogEntriesMap); 0297 if (doNetworking()) { 0298 if (!m_Data->m_Svnclient->log(params, *logs)) { 0299 logs.clear(); 0300 return logs; 0301 } 0302 } else { 0303 svn::InfoEntry e; 0304 if (!singleInfo(m_Data->m_ParentList->baseUri(), svn::Revision::BASE, e)) { 0305 logs.clear(); 0306 return logs; 0307 } 0308 if (svn::Url::isLocal(e.reposRoot().toString())) { 0309 if (!m_Data->m_Svnclient->log(params, *logs)) { 0310 logs.clear(); 0311 return logs; 0312 } 0313 } else { 0314 svn::cache::ReposLog rl(m_Data->m_Svnclient, e.reposRoot().toString()); 0315 QString what; 0316 const QString s1 = e.url().toString().mid(e.reposRoot().toString().length()); 0317 if (which == QLatin1String(".")) { 0318 what = s1; 0319 } else { 0320 const QString s2 = which.mid(m_Data->m_ParentList->baseUri().length()); 0321 what = s1 + QLatin1Char('/') + s2; 0322 } 0323 rl.log(what, start, end, peg, *logs, !follow, limit); 0324 } 0325 } 0326 } catch (const svn::Exception &e) { 0327 emit clientException(e.msg()); 0328 logs.clear(); 0329 } 0330 if (logs && logs->isEmpty()) { 0331 logs.clear(); 0332 emit clientException(i18n("Got no logs")); 0333 } 0334 return logs; 0335 } 0336 0337 bool SvnActions::getSingleLog(svn::LogEntry &t, const svn::Revision &r, const QString &what, const svn::Revision &peg, QString &root) 0338 { 0339 bool res = false; 0340 0341 if (what.isEmpty()) { 0342 return res; 0343 } 0344 if (root.isEmpty()) { 0345 svn::InfoEntry inf; 0346 if (!singleInfo(what, peg, inf)) { 0347 return res; 0348 } 0349 root = inf.reposRoot().toString(); 0350 } 0351 0352 if (!svn::Url::isLocal(root)) { 0353 svn::LogEntriesMap _m; 0354 try { 0355 svn::cache::ReposLog rl(m_Data->m_Svnclient , root); 0356 if (rl.isValid() && rl.simpleLog(_m, r, r, true)) { 0357 const svn::LogEntriesMap::const_iterator it = _m.constFind(r.revnum()); 0358 if (it != _m.constEnd()) { 0359 t = it.value(); 0360 res = true; 0361 } 0362 } 0363 } catch (const svn::Exception &e) { 0364 emit clientException(e.msg()); 0365 } 0366 } 0367 0368 if (!res) { 0369 svn::LogEntriesMapPtr log = getLog(r, r, peg, root, true, 1); 0370 if (log) { 0371 const svn::LogEntriesMap::const_iterator it = log->constFind(r.revnum()); 0372 if (it != log->constEnd()) { 0373 t = it.value(); 0374 res = true; 0375 } 0376 } 0377 } 0378 return res; 0379 } 0380 0381 bool SvnActions::hasMergeInfo(const QString &originpath) 0382 { 0383 QVariant _m(false); 0384 QString path; 0385 0386 svn::InfoEntry e; 0387 if (!singleInfo(originpath, svn::Revision::UNDEFINED, e)) { 0388 return false; 0389 } 0390 path = e.reposRoot().toString(); 0391 if (!m_Data->m_MergeInfoCache.findSingleValid(path, _m)) { 0392 bool mergeinfo; 0393 try { 0394 mergeinfo = m_Data->m_Svnclient->RepoHasCapability(path, svn::CapabilityMergeinfo); 0395 } catch (const svn::ClientException &e) { 0396 emit sendNotify(e.msg()); 0397 return false; 0398 } 0399 _m.setValue(mergeinfo); 0400 m_Data->m_MergeInfoCache.insertKey(_m, path); 0401 } 0402 return _m.toBool(); 0403 } 0404 0405 bool SvnActions::singleInfo(const QString &what, const svn::Revision &_rev, svn::InfoEntry &target, const svn::Revision &_peg) 0406 { 0407 QString url; 0408 QString cacheKey; 0409 QTime d; d.start(); 0410 svn::Revision peg = _peg; 0411 if (!m_Data->m_CurrentContext) { 0412 return false; 0413 } 0414 #ifdef DEBUG_TIMER 0415 QTime _counttime; 0416 _counttime.start(); 0417 #endif 0418 0419 if (!svn::Url::isValid(what)) { 0420 // working copy 0421 // url = svn::Wc::getUrl(what); 0422 url = what; 0423 if (_rev != svn::Revision::WORKING && url.contains(QLatin1Char('@'))) { 0424 url += QStringLiteral("@BASE"); 0425 } 0426 peg = svn::Revision::UNDEFINED; 0427 cacheKey = url; 0428 } else { 0429 // valid url 0430 QUrl _uri(what); 0431 QString prot = svn::Url::transformProtokoll(_uri.scheme()); 0432 _uri.setScheme(prot); 0433 url = _uri.toString(); 0434 if (peg == svn::Revision::UNDEFINED) { 0435 peg = _rev; 0436 } 0437 if (peg == svn::Revision::UNDEFINED) { 0438 peg = svn::Revision::HEAD; 0439 } 0440 cacheKey = _rev.toString() + QLatin1Char('/') + url; 0441 } 0442 svn::InfoEntries e; 0443 bool must_write = false; 0444 0445 { 0446 QReadLocker rl(&(m_Data->m_InfoCacheLock)); 0447 if (cacheKey.isEmpty() || !m_Data->m_InfoCache.findSingleValid(cacheKey, target)) { 0448 must_write = true; 0449 try { 0450 e = (m_Data->m_Svnclient->info(url, svn::DepthEmpty, _rev, peg)); 0451 } catch (const svn::Exception &ce) { 0452 qCDebug(KDESVN_LOG) << "single info: " << ce.msg() << endl; 0453 emit clientException(ce.msg()); 0454 return false; 0455 } 0456 if (e.isEmpty() || e[0].reposRoot().isEmpty()) { 0457 emit clientException(i18n("Got no info.")); 0458 return false; 0459 } 0460 target = e[0]; 0461 } 0462 } 0463 if (must_write) { 0464 QWriteLocker wl(&(m_Data->m_InfoCacheLock)); 0465 if (!cacheKey.isEmpty()) { 0466 m_Data->m_InfoCache.insertKey(e[0], cacheKey); 0467 if (peg != svn::Revision::UNDEFINED && peg.kind() != svn::Revision::NUMBER && peg.kind() != svn::Revision::DATE) { 0468 // for persistent storage, store head into persistent cache makes no sense. 0469 cacheKey = e[0].revision().toString() + QLatin1Char('/') + url; 0470 m_Data->m_InfoCache.insertKey(e[0], cacheKey); 0471 } 0472 } 0473 } 0474 #ifdef DEBUG_TIMER 0475 qCDebug(KDESVN_LOG) << "Time getting info for " << cacheKey << ": " << _counttime.elapsed(); 0476 #endif 0477 0478 return true; 0479 } 0480 0481 void SvnActions::makeTree(const QString &what, const svn::Revision &_rev, const svn::Revision &startr, const svn::Revision &endr) 0482 { 0483 svn::InfoEntry info; 0484 if (!singleInfo(what, _rev, info)) { 0485 return; 0486 } 0487 const QString reposRoot = info.reposRoot().toString(); 0488 0489 if (Kdesvnsettings::fill_cache_on_tree()) { 0490 stopFillCache(); 0491 } 0492 0493 QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("revisiontree_dlg"), m_Data->m_ParentList->realWidget())); 0494 dlg->setWindowTitle(i18nc("@title:window", "History of %1", info.url().toString().mid(reposRoot.length()))); 0495 0496 RevisionTree *rt(new RevisionTree(m_Data->m_Svnclient, m_Data->m_SvnContextListener, reposRoot, 0497 startr, endr, 0498 info.url().toString().mid(reposRoot.length()), _rev, dlg)); 0499 if (rt->isValid()) { 0500 RevTreeWidget *disp = rt->getView(); 0501 if (disp) { 0502 dlg->addWidget(disp); 0503 connect(disp, &RevTreeWidget::makeNorecDiff, 0504 this, &SvnActions::makeNorecDiff); 0505 connect(disp, &RevTreeWidget::makeRecDiff, 0506 this, QOverload<const QString&, const svn::Revision&, const QString&, const svn::Revision&, QWidget*>::of(&SvnActions::makeDiff)); 0507 connect(disp, &RevTreeWidget::makeCat, 0508 this, &SvnActions::slotMakeCat); 0509 dlg->exec(); 0510 } 0511 } 0512 delete dlg; 0513 } 0514 0515 void SvnActions::makeBlame(const svn::Revision &start, const svn::Revision &end, SvnItem *k) 0516 { 0517 if (k) { 0518 makeBlame(start, end, k->fullName(), m_Data->m_ParentList->realWidget()); 0519 } 0520 } 0521 0522 void SvnActions::makeBlame(const svn::Revision &start, const svn::Revision &end, const QString &k, QWidget *_p, const svn::Revision &_peg, SimpleLogCb *_acb) 0523 { 0524 if (!m_Data->m_CurrentContext) { 0525 return; 0526 } 0527 svn::AnnotatedFile blame; 0528 QWidget *_parent = _p ? _p : m_Data->m_ParentList->realWidget(); 0529 bool mergeinfo = hasMergeInfo(m_Data->m_ParentList->baseUri().isEmpty() ? k : m_Data->m_ParentList->baseUri()); 0530 0531 svn::AnnotateParameter params; 0532 params.path(k).pegRevision(_peg == svn::Revision::UNDEFINED ? end : _peg).revisionRange(svn::RevisionRange(start, end)).includeMerged(mergeinfo); 0533 0534 try { 0535 CursorStack a(Qt::BusyCursor); 0536 StopDlg sdlg(m_Data->m_SvnContextListener, _parent, 0537 i18nc("@title:window", "Annotate"), i18n("Annotate lines - hit Cancel for abort")); 0538 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 0539 m_Data->m_Svnclient->annotate(blame, params); 0540 } catch (const svn::Exception &e) { 0541 emit clientException(e.msg()); 0542 return; 0543 } 0544 if (blame.isEmpty()) { 0545 QString ex = i18n("Got no annotate"); 0546 emit clientException(ex); 0547 return; 0548 } 0549 EMIT_FINISHED; 0550 BlameDisplay::displayBlame(_acb ? _acb : this, k, blame, _p); 0551 } 0552 0553 bool SvnActions::makeGet(const svn::Revision &start, const QString &what, const QString &target, 0554 const svn::Revision &peg, QWidget *_dlgparent) 0555 { 0556 if (!m_Data->m_CurrentContext) { 0557 return false; 0558 } 0559 CursorStack a(Qt::BusyCursor); 0560 QWidget *dlgp = _dlgparent ? _dlgparent : m_Data->m_ParentList->realWidget(); 0561 svn::Path p(what); 0562 try { 0563 StopDlg sdlg(m_Data->m_SvnContextListener, dlgp, 0564 i18nc("@title:window", "Content Get"), i18n("Getting content - hit Cancel for abort")); 0565 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 0566 m_Data->m_Svnclient->get(p, target, start, peg); 0567 } catch (const svn::Exception &e) { 0568 emit clientException(e.msg()); 0569 return false; 0570 } catch (...) { 0571 QString ex = i18n("Error getting content"); 0572 emit clientException(ex); 0573 return false; 0574 } 0575 return true; 0576 } 0577 0578 void SvnActions::slotMakeCat(const svn::Revision &start, const QString &what, const QString &disp, const svn::Revision &peg, QWidget *_dlgparent) 0579 { 0580 QTemporaryFile content; 0581 content.setAutoRemove(true); 0582 // required otherwise it will not generate a unique name... 0583 if (!content.open()) { 0584 emit clientException(i18n("Error while open temporary file")); 0585 return; 0586 } 0587 QString tname = content.fileName(); 0588 content.close(); 0589 QWidget *parent = _dlgparent ? _dlgparent : m_Data->m_ParentList->realWidget(); 0590 0591 if (!makeGet(start, what, tname, peg, parent)) { 0592 return; 0593 } 0594 EMIT_FINISHED; 0595 QMimeDatabase db; 0596 const QMimeType mimeType(db.mimeTypeForFile(tname)); 0597 KService::List offers = KMimeTypeTrader::self()->query(mimeType.name(), 0598 QLatin1String("Application"), 0599 QLatin1String("Type == 'Application' or (exist Exec)")); 0600 if (offers.isEmpty() || offers.first()->exec().isEmpty()) { 0601 offers = KMimeTypeTrader::self()->query(mimeType.name(), 0602 QLatin1String("Application"), 0603 QLatin1String("Type == 'Application'")); 0604 } 0605 KService::List::ConstIterator it = offers.constBegin(); 0606 for (; it != offers.constEnd(); ++it) { 0607 if ((*it)->noDisplay()) { 0608 continue; 0609 } 0610 break; 0611 } 0612 if (it != offers.constEnd()) { 0613 content.setAutoRemove(false); 0614 KRun::runService(**it, QList<QUrl>() << QUrl::fromLocalFile(tname), QApplication::activeWindow(), true); 0615 return; 0616 } 0617 0618 QFile file(tname); 0619 file.open(QIODevice::ReadOnly); 0620 const QByteArray co = file.readAll(); 0621 0622 if (!co.isEmpty()) { 0623 QPointer<KSvnSimpleOkDialog> dlg = new KSvnSimpleOkDialog(QStringLiteral("cat_display_dlg"), parent); 0624 dlg->setWindowTitle(i18nc("@title:window", "Content of %1", disp)); 0625 QTextBrowser *ptr = new QTextBrowser(dlg); 0626 ptr->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0627 ptr->setWordWrapMode(QTextOption::NoWrap); 0628 ptr->setReadOnly(true); 0629 ptr->setText(QString::fromUtf8(co, co.size())); 0630 dlg->addWidget(ptr); 0631 dlg->exec(); 0632 delete dlg; 0633 } else { 0634 KMessageBox::information(parent, i18n("Got no content.")); 0635 } 0636 } 0637 0638 bool SvnActions::makeMkdir(const svn::Targets &targets, const QString &logMessage) 0639 { 0640 if (!m_Data->m_CurrentContext || targets.targets().isEmpty()) { 0641 return false; 0642 } 0643 try { 0644 m_Data->m_Svnclient->mkdir(targets, logMessage); 0645 } catch (const svn::Exception &e) { 0646 emit clientException(e.msg()); 0647 return false; 0648 } 0649 return true; 0650 } 0651 0652 QString SvnActions::makeMkdir(const QString &parentDir) 0653 { 0654 if (!m_Data->m_CurrentContext) { 0655 return QString(); 0656 } 0657 bool isOk = false; 0658 const QString ex = QInputDialog::getText(m_Data->m_ParentList->realWidget(), i18n("New folder"), i18n("Enter folder name:"), 0659 QLineEdit::Normal, QString(), &isOk); 0660 if (!isOk || ex.isEmpty()) { 0661 return QString(); 0662 } 0663 svn::Path target(parentDir); 0664 target.addComponent(ex); 0665 0666 try { 0667 m_Data->m_Svnclient->mkdir(target, QString()); 0668 } catch (const svn::Exception &e) { 0669 emit clientException(e.msg()); 0670 return QString(); 0671 } 0672 0673 return target.path(); 0674 } 0675 0676 QString SvnActions::getInfo(const SvnItemList &lst, const svn::Revision &rev, const svn::Revision &peg, bool recursive, bool all) 0677 { 0678 QString res; 0679 for (auto it = lst.cbegin(); it != lst.cend(); ++it) { 0680 if (all) { 0681 res += QStringLiteral("<h4 align=\"center\">%1</h4>").arg((*it)->fullName()); 0682 } 0683 res += getInfo((*it)->fullName(), rev, peg, recursive, all); 0684 } 0685 return res; 0686 } 0687 0688 QString SvnActions::getInfo(const QString &_what, const svn::Revision &rev, const svn::Revision &peg, bool recursive, bool all) 0689 { 0690 if (!m_Data->m_CurrentContext) { 0691 return QString(); 0692 } 0693 svn::InfoEntries entries; 0694 if (recursive) { 0695 try { 0696 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 0697 i18nc("@title:window", "Details"), i18n("Retrieving information - hit Cancel for abort")); 0698 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 0699 QString path = _what; 0700 if (_what.contains(QLatin1Char('@')) && !svn::Url::isValid(_what)) { 0701 path += QLatin1String("@BASE"); 0702 } 0703 entries = (m_Data->m_Svnclient->info(path, 0704 recursive ? svn::DepthInfinity : svn::DepthEmpty, 0705 rev, 0706 peg)); 0707 } catch (const svn::Exception &e) { 0708 emit clientException(e.msg()); 0709 return QString(); 0710 } 0711 } else { 0712 svn::InfoEntry info; 0713 if (!singleInfo(_what, rev, info, peg)) { 0714 return QString(); 0715 } 0716 entries.append(info); 0717 } 0718 return getInfo(entries, _what, all); 0719 } 0720 0721 QString SvnActions::getInfo(const svn::InfoEntries &entries, const QString &_what, bool all) 0722 { 0723 QString text; 0724 static QString rb(QStringLiteral("<tr><td><nobr>")); 0725 static QString re(QStringLiteral("</nobr></td></tr>\n")); 0726 static QString cs(QStringLiteral("</nobr>:</td><td><nobr>")); 0727 unsigned int val = 0; 0728 for (auto it = entries.begin(); it != entries.end(); ++it) { 0729 if (val > 0) { 0730 text += QStringLiteral("<hline>"); 0731 } 0732 ++val; 0733 text += QStringLiteral("<p align=\"center\">"); 0734 text += QStringLiteral("<table cellspacing=0 cellpadding=0>"); 0735 if (!(*it).Name().isEmpty()) { 0736 text += rb + i18n("Name") + cs + ((*it).Name()) + re; 0737 } 0738 if (all) { 0739 text += rb + i18n("URL") + cs + ((*it).url().toDisplayString()) + re; 0740 if (!(*it).reposRoot().toString().isEmpty()) { 0741 text += rb + i18n("Canonical repository URL") + cs + ((*it).reposRoot().toDisplayString()) + re; 0742 } 0743 if (!(*it).checksum().isEmpty()) { 0744 text += rb + i18n("Checksum") + cs + ((*it).checksum()) + re; 0745 } 0746 } 0747 text += rb + i18n("Type") + cs; 0748 switch ((*it).kind()) { 0749 case svn_node_none: 0750 text += i18n("Absent"); 0751 break; 0752 case svn_node_file: 0753 text += i18n("File"); 0754 break; 0755 case svn_node_dir: 0756 text += i18n("Folder"); 0757 break; 0758 case svn_node_unknown: 0759 default: 0760 text += i18n("Unknown"); 0761 break; 0762 } 0763 text += re; 0764 if ((*it).kind() == svn_node_file) { 0765 text += rb + i18n("Size") + cs; 0766 if ((*it).size() != svn::InfoEntry::SVNQT_SIZE_UNKNOWN) { 0767 text += helpers::ByteToString((*it).size()); 0768 } else if ((*it).working_size() != svn::InfoEntry::SVNQT_SIZE_UNKNOWN) { 0769 text += helpers::ByteToString((*it).working_size()); 0770 } 0771 text += re; 0772 } 0773 if (all) { 0774 text += rb + i18n("Schedule") + cs; 0775 switch ((*it).Schedule()) { 0776 case svn_wc_schedule_normal: 0777 text += i18n("Normal"); 0778 break; 0779 case svn_wc_schedule_add: 0780 text += i18n("Addition"); 0781 break; 0782 case svn_wc_schedule_delete: 0783 text += i18n("Deletion"); 0784 break; 0785 case svn_wc_schedule_replace: 0786 text += i18n("Replace"); 0787 break; 0788 default: 0789 text += i18n("Unknown"); 0790 break; 0791 } 0792 text += re; 0793 text += rb + i18n("UUID") + cs + ((*it).uuid()) + re; 0794 } 0795 text += rb + i18n("Last author") + cs + ((*it).cmtAuthor()) + re; 0796 if ((*it).cmtDate().IsValid()) { 0797 text += rb + i18n("Last committed") + cs + (*it).cmtDate().toString() + re; 0798 } 0799 text += rb + i18n("Last revision") + cs + (*it).cmtRev().toString() + re; 0800 if ((*it).textTime().IsValid()) { 0801 text += rb + i18n("Content last changed") + cs + (*it).textTime().toString() + re; 0802 } 0803 if (all) { 0804 if ((*it).propTime().IsValid()) { 0805 text += rb + i18n("Property last changed") + cs + (*it).propTime().toString() + re; 0806 } 0807 for (const auto & _cfi : (*it).conflicts()) { 0808 text += rb + i18n("New version of conflicted file") + cs + (_cfi->theirFile()); 0809 } 0810 if ((*it).prejfile().length()) { 0811 text += rb + i18n("Property reject file") + 0812 cs + ((*it).prejfile()) + re; 0813 } 0814 0815 if (!(*it).copyfromUrl().isEmpty()) { 0816 text += rb + i18n("Copy from URL") + cs + ((*it).copyfromUrl().toDisplayString()) + re; 0817 } 0818 if ((*it).lockEntry().Locked()) { 0819 text += rb + i18n("Lock token") + cs + ((*it).lockEntry().Token()) + re; 0820 text += rb + i18n("Owner") + cs + ((*it).lockEntry().Owner()) + re; 0821 text += rb + i18n("Locked on") + cs + 0822 (*it).lockEntry().Date().toString() + 0823 re; 0824 text += rb + i18n("Lock comment") + cs + 0825 (*it).lockEntry().Comment() + re; 0826 } else { 0827 svn::StatusPtr d; 0828 if (checkReposLockCache(_what, d) && d && d->lockEntry().Locked()) { 0829 text += rb + i18n("Lock token") + cs + (d->lockEntry().Token()) + re; 0830 text += rb + i18n("Owner") + cs + (d->lockEntry().Owner()) + re; 0831 text += rb + i18n("Locked on") + cs + 0832 d->lockEntry().Date().toString() + 0833 re; 0834 text += rb + i18n("Lock comment") + cs + 0835 d->lockEntry().Comment() + re; 0836 } 0837 } 0838 } 0839 text += QStringLiteral("</table></p>\n"); 0840 } 0841 return text; 0842 } 0843 0844 void SvnActions::makeInfo(const SvnItemList &lst, const svn::Revision &rev, const svn::Revision &peg, bool recursive) 0845 { 0846 QStringList infoList; 0847 infoList.reserve(lst.size()); 0848 for (const auto item : lst) { 0849 const QString text = getInfo(item->fullName(), rev, peg, recursive, true); 0850 if (!text.isEmpty()) { 0851 infoList += text; 0852 } 0853 } 0854 showInfo(infoList); 0855 } 0856 0857 void SvnActions::makeInfo(const QStringList &lst, const svn::Revision &rev, const svn::Revision &peg, bool recursive) 0858 { 0859 QStringList infoList; 0860 infoList.reserve(lst.size()); 0861 for (int i = 0; i < lst.size(); ++i) { 0862 const QString text = getInfo(lst.at(i), rev, peg, recursive, true); 0863 if (!text.isEmpty()) { 0864 infoList += text; 0865 } 0866 } 0867 showInfo(infoList); 0868 } 0869 0870 void SvnActions::showInfo(const QStringList &infoList) 0871 { 0872 if (infoList.isEmpty()) { 0873 return; 0874 } 0875 QString text(QLatin1String("<html><head></head><body>")); 0876 for (int i = 0; i < infoList.count(); ++i) { 0877 text += QLatin1String("<h4 align=\"center\">") + infoList.at(i) + QLatin1String("</h4>"); 0878 } 0879 text += QLatin1String("</body></html>"); 0880 0881 QPointer<KSvnSimpleOkDialog> dlg = new KSvnSimpleOkDialog(QStringLiteral("info_dialog"), QApplication::activeModalWidget()); 0882 dlg->setWindowTitle(i18nc("@title:window", "Infolist")); 0883 QTextBrowser *ptr = new QTextBrowser(dlg); 0884 dlg->addWidget(ptr); 0885 ptr->setReadOnly(true); 0886 ptr->setText(text); 0887 dlg->exec(); 0888 delete dlg; 0889 } 0890 0891 0892 void SvnActions::editProperties(SvnItem *k, const svn::Revision &rev) 0893 { 0894 if (!m_Data->m_CurrentContext) { 0895 return; 0896 } 0897 if (!k) { 0898 return; 0899 } 0900 QPointer<PropertiesDlg> dlg(new PropertiesDlg(k, svnclient(), rev)); 0901 connect(dlg, SIGNAL(clientException(QString)), m_Data->m_ParentList->realWidget(), SLOT(slotClientException(QString))); 0902 if (dlg->exec() != QDialog::Accepted) { 0903 delete dlg; 0904 return; 0905 } 0906 svn::PropertiesMap setList; 0907 QStringList delList; 0908 dlg->changedItems(setList, delList); 0909 changeProperties(setList, delList, k->fullName()); 0910 k->refreshStatus(); 0911 EMIT_FINISHED; 0912 delete dlg; 0913 } 0914 0915 bool SvnActions::changeProperties(const svn::PropertiesMap &setList, const QStringList &delList, const QString &path, const svn::Depth &depth) 0916 { 0917 try { 0918 svn::PropertiesParameter params; 0919 params.path(path).depth(depth); 0920 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 0921 i18nc("@title:window", "Applying Properties"), i18n("<center>Applying<br/>hit cancel for abort</center>")); 0922 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 0923 // propertyValue == QString::null -> delete property 0924 for (int pos = 0; pos < delList.size(); ++pos) { 0925 m_Data->m_Svnclient->propset(params.propertyName(delList.at(pos))); 0926 } 0927 for (svn::PropertiesMap::ConstIterator it = setList.begin(); it != setList.end(); ++it) { 0928 m_Data->m_Svnclient->propset(params.propertyName(it.key()).propertyValue(it.value())); 0929 } 0930 } catch (const svn::Exception &e) { 0931 emit clientException(e.msg()); 0932 return false; 0933 } 0934 return true; 0935 } 0936 0937 /*! 0938 \fn SvnActions::slotCommit() 0939 */ 0940 void SvnActions::doCommit(const SvnItemList &which) 0941 { 0942 if (!m_Data->m_CurrentContext || !m_Data->m_ParentList->isWorkingCopy()) { 0943 return; 0944 } 0945 0946 svn::Paths targets; 0947 if (which.isEmpty()) { 0948 targets.push_back(svn::Path(QStringLiteral("."))); 0949 } else { 0950 targets.reserve(which.size()); 0951 for (const SvnItem *item : which) { 0952 targets.push_back(svn::Path( 0953 m_Data->m_ParentList->relativePath(item) 0954 )); 0955 } 0956 } 0957 if (!m_Data->m_ParentList->baseUri().isEmpty()) { 0958 if (!QDir::setCurrent(m_Data->m_ParentList->baseUri())) { 0959 QString msg = i18n("Could not change to folder %1\n", m_Data->m_ParentList->baseUri()) 0960 + QString::fromLocal8Bit(strerror(errno)); 0961 emit sendNotify(msg); 0962 } 0963 } 0964 if (makeCommit(svn::Targets(targets)) && Kdesvnsettings::log_cache_on_open()) { 0965 startFillCache(m_Data->m_ParentList->baseUri(), true); 0966 } 0967 } 0968 0969 bool SvnActions::makeCommit(const svn::Targets &targets) 0970 { 0971 bool ok, keeplocks; 0972 svn::Depth depth; 0973 svn::Revision nnum; 0974 bool review = Kdesvnsettings::review_commit(); 0975 QString msg; 0976 0977 if (!doNetworking()) { 0978 emit clientException(i18n("Not commit because networking is disabled")); 0979 return false; 0980 } 0981 0982 svn::CommitParameter commit_parameters; 0983 stopFillCache(); 0984 if (!review) { 0985 msg = Commitmsg_impl::getLogmessage(&ok, &depth, &keeplocks, m_Data->m_ParentList->realWidget()); 0986 if (!ok) { 0987 return false; 0988 } 0989 commit_parameters.targets(targets); 0990 } else { 0991 CommitActionEntries _check, _uncheck, _result; 0992 svn::StatusEntries _Cache; 0993 depth = svn::DepthEmpty; 0994 svn::StatusParameter params; 0995 params.depth(svn::DepthInfinity).all(false).update(false).noIgnore(false).revision(svn::Revision::HEAD); 0996 /// @todo filter out double entries 0997 for (const auto &tgt : targets.targets()) { 0998 try { 0999 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 1000 i18nc("@title:window", "Status / List"), i18n("Creating list / check status")); 1001 _Cache = m_Data->m_Svnclient->status(params.path(tgt.path())); 1002 } catch (const svn::Exception &e) { 1003 emit clientException(e.msg()); 1004 return false; 1005 } 1006 for (const svn::StatusPtr &ptr : qAsConst(_Cache)) { 1007 const QString _p = ptr->path(); 1008 // check the node status, not the text status (it does not cover the prop status) 1009 if (ptr->isRealVersioned() && ( 1010 ptr->nodeStatus() == svn_wc_status_modified || 1011 ptr->nodeStatus() == svn_wc_status_added || 1012 ptr->nodeStatus() == svn_wc_status_replaced || 1013 ptr->nodeStatus() == svn_wc_status_deleted || 1014 ptr->nodeStatus() == svn_wc_status_modified 1015 )) { 1016 if (ptr->nodeStatus() == svn_wc_status_deleted) { 1017 _check.append(CommitActionEntry(_p, i18n("Delete"), CommitActionEntry::DELETE)); 1018 } else { 1019 _check.append(CommitActionEntry(_p, i18n("Commit"), CommitActionEntry::COMMIT)); 1020 } 1021 } else if (ptr->nodeStatus() == svn_wc_status_missing) { 1022 _uncheck.append(CommitActionEntry(_p, i18n("Delete and Commit"), CommitActionEntry::MISSING_DELETE)); 1023 } else if (!ptr->isVersioned()) { 1024 _uncheck.append(CommitActionEntry(_p, i18n("Add and Commit"), CommitActionEntry::ADD_COMMIT)); 1025 } 1026 } 1027 } 1028 msg = Commitmsg_impl::getLogmessage(_check, _uncheck, this, _result, &ok, &keeplocks, m_Data->m_ParentList->realWidget()); 1029 if (!ok || _result.isEmpty()) { 1030 return false; 1031 } 1032 svn::Paths _add, _commit, _delete; 1033 depth = svn::DepthInfinity; 1034 for (const CommitActionEntry &entry : qAsConst(_result)) { 1035 _commit.append(entry.name()); 1036 if (entry.type() == CommitActionEntry::ADD_COMMIT) { 1037 _add.append(entry.name()); 1038 } else if (entry.type() == CommitActionEntry::MISSING_DELETE) { 1039 _delete.append(entry.name()); 1040 } 1041 } 1042 if (!_add.isEmpty()) { 1043 if (!addItems(_add, svn::DepthEmpty)) { 1044 return false; 1045 } 1046 } 1047 if (!_delete.isEmpty()) { 1048 makeDelete(svn::Targets(_delete)); 1049 } 1050 commit_parameters.targets(svn::Targets(_commit)); 1051 } 1052 commit_parameters.keepLocks(keeplocks).depth(depth).message(msg); 1053 try { 1054 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 1055 i18nc("@title:window", "Commit"), i18n("Commit - hit Cancel for abort")); 1056 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1057 nnum = m_Data->m_Svnclient->commit(commit_parameters); 1058 } catch (const svn::Exception &e) { 1059 emit clientException(e.msg()); 1060 return false; 1061 } 1062 EMIT_REFRESH; 1063 emit sendNotify(i18n("Committed revision %1.", nnum.toString())); 1064 return true; 1065 } 1066 1067 void SvnActions::slotProcessDataRead(const QByteArray &data, WatchedProcess *) 1068 { 1069 emit sendNotify(QString::fromLocal8Bit(data)); 1070 } 1071 1072 bool SvnActions::get(const QString &what, const QString &to, const svn::Revision &rev, const svn::Revision &peg, QWidget *p) 1073 { 1074 svn::Revision _peg = peg; 1075 if (_peg == svn::Revision::UNDEFINED) { 1076 _peg = rev; 1077 } 1078 1079 try { 1080 StopDlg sdlg(m_Data->m_SvnContextListener, p ? p : m_Data->m_ParentList->realWidget(), 1081 i18nc("@title:window", "Downloading"), i18n("Download - hit Cancel for abort")); 1082 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1083 m_Data->m_Svnclient->get(svn::Path(what), 1084 to, rev, _peg); 1085 } catch (const svn::Exception &e) { 1086 emit clientException(e.msg()); 1087 return false; 1088 } 1089 return true; 1090 } 1091 1092 /*! 1093 \fn SvnActions::makeDiff(const QString&,const svn::Revision&start,const svn::Revision&end) 1094 */ 1095 void SvnActions::makeDiff(const QString &what, const svn::Revision &start, const svn::Revision &end, const svn::Revision &_peg, bool isDir) 1096 { 1097 makeDiff(what, start, what, end, _peg, isDir, m_Data->m_ParentList->realWidget()); 1098 } 1099 1100 void SvnActions::makeDiff(const QString &p1, const svn::Revision &start, const QString &p2, const svn::Revision &end) 1101 { 1102 makeDiff(p1, start, p2, end, nullptr); 1103 } 1104 1105 void SvnActions::makeDiff(const QString &p1, const svn::Revision &start, const QString &p2, const svn::Revision &end, QWidget *p) 1106 { 1107 if (!doNetworking() && start != svn::Revision::BASE && end != svn::Revision::WORKING) { 1108 emit sendNotify(i18n("Can not do this diff because networking is disabled.")); 1109 return; 1110 } 1111 if (m_Data->isExternalDiff()) { 1112 svn::InfoEntry info; 1113 if (singleInfo(p1, start, info)) { 1114 makeDiff(p1, start, p2, end, end, info.isDir(), p); 1115 } 1116 return; 1117 } 1118 makeDiffinternal(p1, start, p2, end, p); 1119 } 1120 1121 void SvnActions::makeDiffExternal(const QString &p1, const svn::Revision &start, const QString &p2, const svn::Revision &end, const svn::Revision &_peg, 1122 bool isDir, QWidget *p, bool rec) 1123 { 1124 QFileInfo f1(p1); 1125 QFileInfo f2(p2); 1126 QTemporaryFile tfile(QDir::tempPath() + QLatin1Char('/') + f1.fileName() + QLatin1Char('-') + start.toString()); 1127 QTemporaryFile tfile2(QDir::tempPath() + QLatin1Char('/') + f2.fileName() + QLatin1Char('-') + end.toString()); 1128 1129 QString s1 = f1.fileName() + QLatin1Char('-') + start.toString(); 1130 QString s2 = f2.fileName() + QLatin1Char('-') + end.toString(); 1131 if (f1.fileName() == f2.fileName() && p1 != p2) { 1132 s2.append(QStringLiteral("-sec")); 1133 } 1134 QTemporaryDir tdir1; 1135 tdir1.setAutoRemove(true); 1136 tfile.setAutoRemove(true); 1137 tfile2.setAutoRemove(true); 1138 1139 tfile.open(); 1140 tfile2.open(); 1141 1142 QString first, second; 1143 svn::Revision peg = _peg; 1144 1145 if (start != svn::Revision::WORKING) { 1146 first = isDir ? tdir1.path() + QLatin1Char('/') + s1 : tfile.fileName(); 1147 } else { 1148 first = p1; 1149 } 1150 if (end != svn::Revision::WORKING) { 1151 second = isDir ? tdir1.path() + QLatin1Char('/') + s2 : tfile2.fileName(); 1152 } else { 1153 second = p2; 1154 } 1155 if (second == first) { 1156 KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("Both entries seems to be the same, can not diff.")); 1157 return; 1158 } 1159 if (start != svn::Revision::WORKING) { 1160 if (!isDir) { 1161 if (!get(p1, tfile.fileName(), start, peg, p)) { 1162 return; 1163 } 1164 } else { 1165 if (!makeCheckout(p1, first, start, peg, 1166 rec ? svn::DepthInfinity : svn::DepthFiles, true, false, false, false, false, p)) { 1167 return; 1168 } 1169 } 1170 } 1171 if (end != svn::Revision::WORKING) { 1172 if (!isDir) { 1173 if (!get(p2, tfile2.fileName(), end, peg, p)) { 1174 return; 1175 } 1176 } else { 1177 if (!makeCheckout(p2, second, end, peg, 1178 rec ? svn::DepthInfinity : svn::DepthFiles, true, false, false, false, false, p)) { 1179 return; 1180 } 1181 } 1182 } 1183 1184 const QString edisp = Kdesvnsettings::external_diff_display(); 1185 const QVector<QStringRef> wlist = edisp.splitRef(QLatin1Char(' ')); 1186 WatchedProcess *proc = new WatchedProcess(this); 1187 for (auto it = wlist.begin(); it != wlist.end(); ++it) { 1188 if (*it == QLatin1String("%1")) { 1189 *proc << first; 1190 } else if (*it == QLatin1String("%2")) { 1191 *proc << second; 1192 } else { 1193 *proc << (*it).toString(); 1194 } 1195 } 1196 proc->setAutoDelete(true); 1197 proc->setOutputChannelMode(KProcess::MergedChannels); 1198 connect(proc, &WatchedProcess::dataStderrRead, 1199 this, &SvnActions::slotProcessDataRead); 1200 connect(proc, &WatchedProcess::dataStdoutRead, 1201 this, &SvnActions::slotProcessDataRead); 1202 if (!isDir) { 1203 tfile2.setAutoRemove(false); 1204 tfile.setAutoRemove(false); 1205 proc->appendTempFile(tfile.fileName()); 1206 proc->appendTempFile(tfile2.fileName()); 1207 } else { 1208 tdir1.setAutoRemove(false); 1209 proc->appendTempDir(tdir1.path()); 1210 } 1211 tfile.close(); 1212 tfile2.close(); 1213 1214 proc->start(); 1215 if (proc->waitForStarted(-1)) { 1216 if (m_Data->runblocked) { 1217 proc->waitForFinished(-1); 1218 } 1219 return; 1220 } else { 1221 emit sendNotify(i18n("Diff-process could not started, check command.")); 1222 } 1223 } 1224 1225 void SvnActions::makeDiff(const QString &p1, const svn::Revision &start, const QString &p2, const svn::Revision &end, const svn::Revision &_peg, bool isDir, QWidget *p) 1226 { 1227 if (m_Data->isExternalDiff()) { 1228 makeDiffExternal(p1, start, p2, end, _peg, isDir, p); 1229 } else { 1230 makeDiffinternal(p1, start, p2, end, p, _peg); 1231 } 1232 } 1233 1234 void SvnActions::makeDiffinternal(const QString &p1, const svn::Revision &r1, const QString &p2, const svn::Revision &r2, QWidget *p, const svn::Revision &_peg) 1235 { 1236 if (!m_Data->m_CurrentContext) { 1237 return; 1238 } 1239 QByteArray ex; 1240 QTemporaryDir tdir; 1241 tdir.setAutoRemove(true); 1242 QString tn(tdir.path() + QLatin1String("/svndiff")); 1243 QDir d1(tdir.path()); 1244 d1.mkdir(QStringLiteral("svndiff")); 1245 bool ignore_content = Kdesvnsettings::diff_ignore_content(); 1246 bool gitformat = Kdesvnsettings::diff_gitformat_default(); 1247 bool copy_as_add = Kdesvnsettings::diff_copies_as_add(); 1248 QWidget *parent = p ? p : m_Data->m_ParentList->realWidget(); 1249 QStringList extraOptions; 1250 if (Kdesvnsettings::diff_ignore_spaces()) { 1251 extraOptions.append(QStringLiteral("-b")); 1252 } 1253 if (Kdesvnsettings::diff_ignore_all_white_spaces()) { 1254 extraOptions.append(QStringLiteral("-w")); 1255 } 1256 svn::Revision peg = _peg == svn::Revision::UNDEFINED ? r2 : _peg; 1257 svn::DiffParameter _opts; 1258 _opts.path1(p1).path2(p2).tmpPath(tn). 1259 peg(peg).rev1(r1).rev2(r2). 1260 ignoreContentType(ignore_content).extra(svn::StringArray(extraOptions)).depth(svn::DepthInfinity).ignoreAncestry(false).noDiffDeleted(false).changeList(svn::StringArray()). 1261 git_diff_format(gitformat).copies_as_adds(copy_as_add); 1262 1263 try { 1264 StopDlg sdlg(m_Data->m_SvnContextListener, parent, 1265 i18nc("@title:window", "Diffing"), i18n("Diffing - hit Cancel for abort")); 1266 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1267 if (p1 == p2 && (r1.isRemote() || r2.isRemote())) { 1268 ex = m_Data->m_Svnclient->diff_peg(_opts); 1269 } else { 1270 ex = m_Data->m_Svnclient->diff(_opts.relativeTo(p1 == p2 ? svn::Path(p1) : svn::Path())); 1271 } 1272 } catch (const svn::Exception &e) { 1273 emit clientException(e.msg()); 1274 return; 1275 } 1276 EMIT_FINISHED; 1277 if (ex.isEmpty()) { 1278 emit clientException(i18n("No difference to display")); 1279 return; 1280 } 1281 dispDiff(ex); 1282 } 1283 1284 void SvnActions::makeNorecDiff(const QString &p1, const svn::Revision &r1, const QString &p2, const svn::Revision &r2, QWidget *_p) 1285 { 1286 if (!m_Data->m_CurrentContext) { 1287 return; 1288 } 1289 if (m_Data->isExternalDiff()) { 1290 svn::InfoEntry info; 1291 if (singleInfo(p1, r1, info)) { 1292 makeDiffExternal(p1, r1, p2, r2, r2, info.isDir(), _p, false); 1293 } 1294 return; 1295 } 1296 QStringList extraOptions; 1297 if (Kdesvnsettings::diff_ignore_spaces()) { 1298 extraOptions.append(QStringLiteral("-b")); 1299 } 1300 if (Kdesvnsettings::diff_ignore_all_white_spaces()) { 1301 extraOptions.append(QStringLiteral("-w")); 1302 } 1303 QByteArray ex; 1304 QTemporaryDir tdir; 1305 tdir.setAutoRemove(true); 1306 QString tn(tdir.path() + QLatin1String("/svndiff")); 1307 QDir d1(tdir.path()); 1308 d1.mkdir(QStringLiteral("svndiff")); 1309 bool ignore_content = Kdesvnsettings::diff_ignore_content(); 1310 svn::DiffParameter _opts; 1311 // no peg revision required 1312 _opts.path1(p1).path2(p2).tmpPath(tn). 1313 rev1(r1).rev2(r2). 1314 ignoreContentType(ignore_content).extra(svn::StringArray(extraOptions)).depth(svn::DepthEmpty).ignoreAncestry(false).noDiffDeleted(false).changeList(svn::StringArray()); 1315 1316 try { 1317 StopDlg sdlg(m_Data->m_SvnContextListener, _p ? _p : m_Data->m_ParentList->realWidget(), 1318 i18nc("@title:window", "Diffing"), i18n("Diffing - hit cancel for abort")); 1319 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1320 ex = m_Data->m_Svnclient->diff(_opts); 1321 } catch (const svn::Exception &e) { 1322 emit clientException(e.msg()); 1323 return; 1324 } 1325 EMIT_FINISHED; 1326 if (ex.isEmpty()) { 1327 emit clientException(i18n("No difference to display")); 1328 return; 1329 } 1330 1331 dispDiff(ex); 1332 } 1333 1334 void SvnActions::dispDiff(const QByteArray &ex) 1335 { 1336 QString what = Kdesvnsettings::external_diff_display(); 1337 1338 if (Kdesvnsettings::use_external_diff() && (!what.contains(QLatin1String("%1")) || !what.contains(QLatin1String("%2")))) { 1339 const QVector<QStringRef> wlist = what.splitRef(QLatin1Char(' ')); 1340 WatchedProcess *proc = new WatchedProcess(this); 1341 bool fname_used = false; 1342 1343 for (auto it = wlist.begin(); it != wlist.end(); ++it) { 1344 if (*it == QLatin1String("%f")) { 1345 QTemporaryFile tfile; 1346 tfile.setAutoRemove(false); 1347 tfile.open(); 1348 fname_used = true; 1349 QDataStream ds(&tfile); 1350 ds.writeRawData(ex, ex.size()); 1351 *proc << tfile.fileName(); 1352 proc->appendTempFile(tfile.fileName()); 1353 tfile.close(); 1354 } else { 1355 *proc << (*it).toString(); 1356 } 1357 } 1358 proc->setAutoDelete(true); 1359 proc->setOutputChannelMode(KProcess::MergedChannels); 1360 connect(proc, &WatchedProcess::dataStderrRead, 1361 this, &SvnActions::slotProcessDataRead); 1362 connect(proc, &WatchedProcess::dataStdoutRead, 1363 this, &SvnActions::slotProcessDataRead); 1364 1365 proc->start(); 1366 if (proc->waitForStarted(-1)) { 1367 if (!fname_used) { 1368 proc->write(ex); 1369 proc->closeWriteChannel(); 1370 } 1371 if (m_Data->runblocked) { 1372 proc->waitForFinished(-1); 1373 } 1374 return; 1375 } else { 1376 emit sendNotify(i18n("Display process could not started, check command.")); 1377 } 1378 } 1379 bool need_modal = m_Data->runblocked || QApplication::activeModalWidget() != nullptr; 1380 if (need_modal || !m_Data->m_DiffBrowserPtr || !m_Data->m_DiffDialog) { 1381 1382 if (!need_modal && m_Data->m_DiffBrowserPtr) { 1383 delete m_Data->m_DiffBrowserPtr; 1384 } 1385 QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("diff_display"))); 1386 if (!need_modal) { 1387 dlg->setParent(nullptr); 1388 } 1389 dlg->setWindowTitle(i18nc("@title:window", "Diff Display")); 1390 DiffBrowser *ptr(new DiffBrowser(dlg)); 1391 ptr->setText(ex); 1392 dlg->addWidget(ptr); 1393 EncodingSelector_impl *enc(new EncodingSelector_impl(dlg)); 1394 dlg->addWidget(enc); 1395 connect(enc, &EncodingSelector_impl::TextCodecChanged, 1396 ptr, &DiffBrowser::slotTextCodecChanged); 1397 enc->setCurrentEncoding(Kdesvnsettings::locale_for_diff()); 1398 // saveAs 1399 QPushButton *pbSaveAs = new QPushButton(dlg->buttonBox()); 1400 KStandardGuiItem::assign(pbSaveAs, KStandardGuiItem::SaveAs); 1401 dlg->buttonBox()->addButton(pbSaveAs, QDialogButtonBox::ActionRole); 1402 connect(pbSaveAs, &QAbstractButton::clicked, ptr, &DiffBrowser::saveDiff); 1403 1404 dlg->buttonBox()->setStandardButtons(QDialogButtonBox::Close); 1405 dlg->addButtonBox(); 1406 if (need_modal) { 1407 ptr->setFocus(); 1408 dlg->exec(); 1409 delete dlg; 1410 return; 1411 } else { 1412 m_Data->m_DiffBrowserPtr = ptr; 1413 m_Data->m_DiffDialog = dlg; 1414 } 1415 } else { 1416 m_Data->m_DiffBrowserPtr->setText(ex); 1417 m_Data->m_DiffBrowserPtr->setFocus(); 1418 } 1419 if (m_Data->m_DiffDialog) { 1420 m_Data->m_DiffDialog->show(); 1421 m_Data->m_DiffDialog->raise(); 1422 } 1423 } 1424 1425 1426 /*! 1427 \fn SvnActions::makeUpdate(const QString&what,const svn::Revision&rev,bool recurse) 1428 */ 1429 void SvnActions::makeUpdate(const svn::Targets &targets, const svn::Revision &rev, svn::Depth depth) 1430 { 1431 if (!m_Data->m_CurrentContext) { 1432 return; 1433 } 1434 svn::Revisions ret; 1435 stopCheckUpdateThread(); 1436 try { 1437 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 1438 i18nc("@title:window", "Making update"), i18n("Making update - hit Cancel for abort")); 1439 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1440 svn::UpdateParameter _params; 1441 m_Data->m_SvnContextListener->cleanUpdatedItems(); 1442 _params.targets(targets).revision(rev).depth(depth).ignore_externals(false).allow_unversioned(false).sticky_depth(true); 1443 ret = m_Data->m_Svnclient->update(_params); 1444 } catch (const svn::Exception &e) { 1445 emit clientException(e.msg()); 1446 return; 1447 } 1448 removeFromUpdateCache(m_Data->m_SvnContextListener->updatedItems(), true); 1449 //removeFromUpdateCache(what,depth==svn::DepthFiles); 1450 EMIT_REFRESH; 1451 EMIT_FINISHED; 1452 m_Data->clearCaches(); 1453 } 1454 1455 /*! 1456 \fn SvnActions::slotUpdateHeadRec() 1457 */ 1458 void SvnActions::slotUpdateHeadRec() 1459 { 1460 prepareUpdate(false); 1461 } 1462 1463 1464 /*! 1465 \fn SvnActions::prepareUpdate(bool ask) 1466 */ 1467 void SvnActions::prepareUpdate(bool ask) 1468 { 1469 if (!m_Data->m_ParentList || !m_Data->m_ParentList->isWorkingCopy()) { 1470 return; 1471 } 1472 const SvnItemList k = m_Data->m_ParentList->SelectionList(); 1473 1474 svn::Paths what; 1475 if (k.isEmpty()) { 1476 what.append(svn::Path(m_Data->m_ParentList->baseUri())); 1477 } else { 1478 what.reserve(k.size()); 1479 for (const SvnItem *item : k) 1480 what.append(svn::Path(item->fullName())); 1481 } 1482 svn::Revision r(svn::Revision::HEAD); 1483 if (ask) { 1484 Rangeinput_impl::revision_range range; 1485 if (!Rangeinput_impl::getRevisionRange(range, true, true)) { 1486 return; 1487 } 1488 r = range.first; 1489 } 1490 makeUpdate(svn::Targets(what), r, svn::DepthUnknown); 1491 } 1492 1493 1494 /*! 1495 \fn SvnActions::slotUpdateTo() 1496 */ 1497 void SvnActions::slotUpdateTo() 1498 { 1499 prepareUpdate(true); 1500 } 1501 1502 1503 /*! 1504 \fn SvnActions::slotAdd() 1505 */ 1506 void SvnActions::slotAdd() 1507 { 1508 makeAdd(false); 1509 } 1510 1511 void SvnActions::slotAddRec() 1512 { 1513 makeAdd(true); 1514 } 1515 1516 void SvnActions::makeAdd(bool rec) 1517 { 1518 if (!m_Data->m_CurrentContext) { 1519 return; 1520 } 1521 if (!m_Data->m_ParentList) { 1522 return; 1523 } 1524 const SvnItemList lst = m_Data->m_ParentList->SelectionList(); 1525 if (lst.isEmpty()) { 1526 KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("Which files or directories should I add?")); 1527 return; 1528 } 1529 svn::Paths items; 1530 items.reserve(lst.size()); 1531 for (const SvnItem *cur : lst) { 1532 if (cur->isVersioned()) { 1533 KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("<center>The entry<br/>%1<br/>is versioned - break.</center>", 1534 cur->fullName())); 1535 return; 1536 } 1537 items.push_back(svn::Path(cur->fullName())); 1538 } 1539 addItems(items, (rec ? svn::DepthInfinity : svn::DepthEmpty)); 1540 emit sigRefreshCurrent(nullptr); 1541 } 1542 1543 bool SvnActions::addItems(const svn::Paths &items, svn::Depth depth) 1544 { 1545 try { 1546 svn::Paths::const_iterator piter; 1547 for (piter = items.begin(); piter != items.end(); ++piter) { 1548 m_Data->m_Svnclient->add((*piter), depth); 1549 } 1550 } catch (const svn::Exception &e) { 1551 emit clientException(e.msg()); 1552 return false; 1553 } 1554 return true; 1555 } 1556 1557 bool SvnActions::makeDelete(const QStringList &w) 1558 { 1559 KMessageBox::ButtonCode answer = KMessageBox::questionYesNoList(nullptr, 1560 i18n("Really delete these entries?"), 1561 w, 1562 i18n("Delete from repository")); 1563 if (answer != KMessageBox::Yes) { 1564 return false; 1565 } 1566 return makeDelete(svn::Targets::fromStringList(w)); 1567 } 1568 1569 /*! 1570 \fn SvnActions::makeDelete() 1571 */ 1572 bool SvnActions::makeDelete(const svn::Targets &target, bool keep_local, bool force) 1573 { 1574 if (!m_Data->m_CurrentContext) { 1575 return false; 1576 } 1577 try { 1578 m_Data->m_Svnclient->remove(target, force, keep_local); 1579 } catch (const svn::Exception &e) { 1580 emit clientException(e.msg()); 1581 return false; 1582 } 1583 EMIT_FINISHED; 1584 return true; 1585 } 1586 1587 void SvnActions::slotCheckout() 1588 { 1589 CheckoutExport(QUrl(), false); 1590 } 1591 1592 void SvnActions::slotExport() 1593 { 1594 CheckoutExport(QUrl(), true); 1595 } 1596 1597 void SvnActions::slotCheckoutCurrent() 1598 { 1599 CheckoutExportCurrent(false); 1600 } 1601 1602 void SvnActions::slotExportCurrent() 1603 { 1604 CheckoutExportCurrent(true); 1605 } 1606 1607 void SvnActions::CheckoutExport(const QUrl &what, bool _exp, bool urlisTarget) 1608 { 1609 QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("checkout_export_dialog"))); 1610 CheckoutInfo_impl *ptr(new CheckoutInfo_impl(dlg)); 1611 dlg->setWindowTitle(_exp ? i18nc("@title:window", "Export a Repository") : i18nc("@title:window", "Checkout a Repository")); 1612 dlg->setWithCancelButton(); 1613 1614 if (!what.isEmpty()) { 1615 if (!urlisTarget) { 1616 ptr->setStartUrl(what); 1617 } else { 1618 ptr->setTargetUrl(what); 1619 } 1620 } 1621 ptr->hideIgnoreKeywords(!_exp); 1622 ptr->hideOverwrite(!_exp); 1623 dlg->addWidget(ptr); 1624 if (dlg->exec() == QDialog::Accepted) { 1625 svn::Revision r = ptr->toRevision(); 1626 bool openit = ptr->openAfterJob(); 1627 bool ignoreExternal = ptr->ignoreExternals(); 1628 if (!ptr->reposURL().isValid()) { 1629 KMessageBox::error(QApplication::activeModalWidget(), i18n("Invalid url given!"), 1630 _exp ? i18n("Export repository") : i18n("Checkout a repository")); 1631 delete dlg; 1632 return; 1633 } 1634 // svn::Path should not take a QString but a QByteArray ... 1635 const QString rUrl(QString::fromUtf8(ptr->reposURL().toEncoded())); 1636 makeCheckout(rUrl, 1637 ptr->targetDir(), r, r, 1638 ptr->getDepth(), 1639 _exp, 1640 openit, 1641 ignoreExternal, 1642 ptr->overwrite(), 1643 ptr->ignoreKeywords(), 1644 nullptr); 1645 } 1646 delete dlg; 1647 } 1648 1649 void SvnActions::CheckoutExportCurrent(bool _exp) 1650 { 1651 // checkout export only on repo, not wc 1652 if (!m_Data->m_ParentList || m_Data->m_ParentList->isWorkingCopy()) { 1653 return; 1654 } 1655 SvnItem *k = m_Data->m_ParentList->Selected(); 1656 if (k && !k->isDir()) { 1657 KMessageBox::error(m_Data->m_ParentList->realWidget(), _exp ? i18n("Exporting a file?") : i18n("Checking out a file?")); 1658 return; 1659 } 1660 QUrl what; 1661 if (!k) { 1662 what = QUrl(m_Data->m_ParentList->baseUri()); 1663 } else { 1664 what = QUrl(k->fullName()); 1665 } 1666 // what is always remote, so QUrl(what) is fine 1667 CheckoutExport(QUrl(what), _exp); 1668 } 1669 1670 bool SvnActions::makeCheckout(const QString &rUrl, const QString &tPath, const svn::Revision &r, const svn::Revision &_peg, 1671 svn::Depth depth, 1672 // kind of operation 1673 bool _exp, 1674 // open after job 1675 bool openIt, 1676 // ignore externals 1677 bool ignoreExternal, 1678 // overwrite/force not versioned items 1679 bool overwrite, 1680 // do not replace svn:keywords on export 1681 bool ignoreKeywords, 1682 QWidget *_p 1683 ) 1684 { 1685 QString fUrl = rUrl; 1686 while (fUrl.endsWith(QLatin1Char('/'))) { 1687 fUrl.chop(1); 1688 } 1689 // can only be a local target dir 1690 svn::Path p(tPath); 1691 svn::Revision peg = _peg; 1692 if (r != svn::Revision::BASE && r != svn::Revision::WORKING && _peg == svn::Revision::UNDEFINED) { 1693 peg = r; 1694 } 1695 if (!_exp || !m_Data->m_CurrentContext) { 1696 reInitClient(); 1697 } 1698 svn::CheckoutParameter cparams; 1699 cparams.moduleName(fUrl).destination(p).revision(r).peg(peg).depth(depth).ignoreExternals(ignoreExternal).overWrite(overwrite).ignoreKeywords(ignoreKeywords); 1700 1701 try { 1702 StopDlg sdlg(m_Data->m_SvnContextListener, _p ? _p : m_Data->m_ParentList->realWidget(), 1703 _exp ? i18nc("@title:window", "Export") : i18nc("@title:window", "Checkout"), _exp ? i18n("Exporting") : i18n("Checking out")); 1704 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1705 if (_exp) { 1706 /// @todo setup parameter for export operation 1707 m_Data->m_Svnclient->doExport(cparams.nativeEol(QString())); 1708 } else { 1709 m_Data->m_Svnclient->checkout(cparams); 1710 } 1711 } catch (const svn::Exception &e) { 1712 emit clientException(e.msg()); 1713 return false; 1714 } 1715 if (openIt) { 1716 const QUrl url(QUrl::fromLocalFile(tPath)); 1717 if (!_exp) { 1718 emit sigGotourl(url); 1719 } else { 1720 QDesktopServices::openUrl(url); 1721 } 1722 } 1723 EMIT_FINISHED; 1724 1725 return true; 1726 } 1727 1728 void SvnActions::slotRevert() 1729 { 1730 if (!m_Data->m_ParentList || !m_Data->m_ParentList->isWorkingCopy()) { 1731 return; 1732 } 1733 const SvnItemList lst = m_Data->m_ParentList->SelectionList(); 1734 QStringList displist; 1735 if (!lst.isEmpty()) { 1736 svn::StatusParameter params; 1737 params.depth(svn::DepthInfinity).all(false).update(false).noIgnore(false).revision(svn::Revision::HEAD); 1738 for (const SvnItem *cur : lst) { 1739 if (!cur->isVersioned()) { 1740 KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("<center>The entry<br/>%1<br/>is not versioned - break.</center>", cur->fullName())); 1741 return; 1742 } 1743 displist.append(cur->fullName()); 1744 } 1745 } else { 1746 displist.push_back(m_Data->m_ParentList->baseUri()); 1747 } 1748 1749 slotRevertItems(displist); 1750 EMIT_REFRESH; 1751 } 1752 1753 void SvnActions::slotRevertItems(const QStringList &displist) 1754 { 1755 if (!m_Data->m_CurrentContext) { 1756 return; 1757 } 1758 if (displist.isEmpty()) { 1759 return; 1760 } 1761 1762 QPointer<RevertForm> dlg(new RevertForm(displist, QApplication::activeModalWidget())); 1763 if (dlg->exec() != QDialog::Accepted) { 1764 delete dlg; 1765 return; 1766 } 1767 const svn::Depth depth = dlg->getDepth(); 1768 delete dlg; 1769 1770 const svn::Targets target(svn::Targets::fromStringList(displist)); 1771 try { 1772 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 1773 i18nc("@title:window", "Revert"), i18n("Reverting items")); 1774 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1775 m_Data->m_Svnclient->revert(target, depth); 1776 } catch (const svn::Exception &e) { 1777 emit clientException(e.msg()); 1778 return; 1779 } 1780 // remove them from cache 1781 for (const auto &tgt : target.targets()) { 1782 m_Data->m_Cache.deleteKey(tgt.path(), depth != svn::DepthInfinity); 1783 } 1784 emit sigItemsReverted(displist); 1785 EMIT_FINISHED; 1786 } 1787 1788 bool SvnActions::makeSwitch(const QUrl &rUrl, 1789 const QString &tPath, 1790 const svn::Revision &r, 1791 svn::Depth depth, 1792 const svn::Revision &peg, 1793 bool stickydepth, 1794 bool ignore_externals, 1795 bool allow_unversioned) 1796 { 1797 if (!m_Data->m_CurrentContext) { 1798 return false; 1799 } 1800 svn::Path p(tPath); 1801 try { 1802 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 1803 i18nc("@title:window", "Switch URL"), i18n("Switching URL")); 1804 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1805 m_Data->m_Svnclient->doSwitch(p, svn::Url(rUrl), r, depth, peg, stickydepth, ignore_externals, allow_unversioned); 1806 } catch (const svn::Exception &e) { 1807 emit clientException(e.msg()); 1808 return false; 1809 } 1810 m_Data->clearCaches(); 1811 EMIT_FINISHED; 1812 return true; 1813 } 1814 1815 bool SvnActions::makeRelocate(const QUrl &fUrl, const QUrl &tUrl, const QString &path, bool recursive, bool ignore_externals) 1816 { 1817 if (!m_Data->m_CurrentContext) { 1818 return false; 1819 } 1820 svn::Path p(path); 1821 try { 1822 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 1823 i18nc("@title:window", "Relocate Repository"), i18n("Relocate repository to new URL")); 1824 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1825 m_Data->m_Svnclient->relocate(p, svn::Url(fUrl), svn::Url(tUrl), recursive, ignore_externals); 1826 } catch (const svn::Exception &e) { 1827 emit clientException(e.msg()); 1828 return false; 1829 } 1830 m_Data->clearCaches(); 1831 EMIT_FINISHED; 1832 return true; 1833 } 1834 1835 void SvnActions::slotSwitch() 1836 { 1837 if (!m_Data->m_CurrentContext) { 1838 return; 1839 } 1840 if (!m_Data->m_ParentList || !m_Data->m_ParentList->isWorkingCopy()) { 1841 return; 1842 } 1843 1844 const SvnItemList lst = m_Data->m_ParentList->SelectionList(); 1845 if (lst.count() > 1) { 1846 KMessageBox::error(nullptr, i18n("Can only switch one item at time")); 1847 return; 1848 } 1849 1850 SvnItem *k = m_Data->m_ParentList->SelectedOrMain(); 1851 if (!k) { 1852 KMessageBox::error(nullptr, i18n("Error getting entry to switch")); 1853 return; 1854 } 1855 const QUrl what = k->Url(); 1856 if (makeSwitch(k->fullName(), what)) { 1857 emit reinitItem(k); 1858 } 1859 } 1860 1861 bool SvnActions::makeSwitch(const QString &path, const QUrl &what) 1862 { 1863 QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("switch_url_dlg"))); 1864 CheckoutInfo_impl *ptr(new CheckoutInfo_impl(dlg)); 1865 dlg->setWindowTitle(i18nc("@title:window", "Switch URL")); 1866 dlg->setWithCancelButton(); 1867 ptr->setStartUrl(what); 1868 ptr->disableAppend(true); 1869 ptr->disableTargetDir(true); 1870 ptr->disableOpen(true); 1871 dlg->addWidget(ptr); 1872 bool done = false; 1873 if (dlg->exec() == QDialog::Accepted) { 1874 if (!ptr->reposURL().isValid()) { 1875 KMessageBox::error(QApplication::activeModalWidget(), i18n("Invalid url given!"), 1876 i18n("Switch URL")); 1877 delete dlg; 1878 return false; 1879 } 1880 svn::Revision r = ptr->toRevision(); 1881 done = makeSwitch(ptr->reposURL(), path, r, ptr->getDepth(), r, true, ptr->ignoreExternals(), ptr->overwrite()); 1882 } 1883 delete dlg; 1884 return done; 1885 } 1886 1887 bool SvnActions::makeCleanup(const QString &path) 1888 { 1889 if (!m_Data->m_CurrentContext) { 1890 return false; 1891 } 1892 try { 1893 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 1894 i18nc("@title:window", "Cleanup"), i18n("Cleaning up folder")); 1895 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1896 m_Data->m_Svnclient->cleanup(svn::Path(path)); 1897 } catch (const svn::Exception &e) { 1898 emit clientException(e.msg()); 1899 return false; 1900 } 1901 return true; 1902 } 1903 1904 void SvnActions::slotResolved(const QString &path) 1905 { 1906 if (!m_Data->m_CurrentContext) { 1907 return; 1908 } 1909 try { 1910 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 1911 i18nc("@title:window", "Resolve"), i18n("Marking resolved")); 1912 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1913 m_Data->m_Svnclient->resolve(svn::Path(path), svn::DepthEmpty); 1914 } catch (const svn::Exception &e) { 1915 emit clientException(e.msg()); 1916 return; 1917 } 1918 m_Data->m_conflictCache.deleteKey(path, false); 1919 emit sigRefreshItem(path); 1920 } 1921 1922 void SvnActions::slotResolve(const QString &p) 1923 { 1924 if (!m_Data->m_CurrentContext) { 1925 return; 1926 } 1927 const QString eresolv = Kdesvnsettings::conflict_resolver(); 1928 const QVector<QStringRef> wlist = eresolv.splitRef(QLatin1Char(' ')); 1929 if (wlist.isEmpty()) { 1930 return; 1931 } 1932 svn::InfoEntry i1; 1933 if (!singleInfo(p, svn::Revision::UNDEFINED, i1)) { 1934 return; 1935 } 1936 QFileInfo fi(p); 1937 QString base; 1938 if (fi.isRelative()) { 1939 base = fi.absolutePath() + QLatin1Char('/'); 1940 } 1941 if (i1.conflicts().isEmpty()) 1942 { 1943 emit sendNotify(i18n("Could not retrieve conflict information - giving up.")); 1944 return; 1945 } 1946 1947 WatchedProcess *proc = new WatchedProcess(this); 1948 for (auto it = wlist.begin(); it != wlist.end(); ++it) { 1949 if (*it == QLatin1String("%o") || *it == QLatin1String("%l")) { 1950 *proc << i1.conflicts()[0]->baseFile(); 1951 } else if (*it == QLatin1String("%m") || *it == QLatin1String("%w")) { 1952 *proc << i1.conflicts()[0]->myFile(); 1953 } else if (*it == QLatin1String("%n") || *it == QLatin1String("%r")) { 1954 *proc << i1.conflicts()[0]->theirFile(); 1955 } else if (*it == QLatin1String("%t")) { 1956 *proc << p; 1957 } else { 1958 *proc << (*it).toString(); 1959 } 1960 } 1961 proc->setAutoDelete(true); 1962 proc->setOutputChannelMode(KProcess::MergedChannels); 1963 connect(proc, &WatchedProcess::dataStderrRead, 1964 this, &SvnActions::slotProcessDataRead); 1965 connect(proc, &WatchedProcess::dataStdoutRead, 1966 this, &SvnActions::slotProcessDataRead); 1967 1968 proc->start(); 1969 if (!proc->waitForStarted(-1)) { 1970 emit sendNotify(i18n("Resolve-process could not started, check command.")); 1971 } 1972 } 1973 1974 void SvnActions::slotImport(const QString &path, const QUrl &target, const QString &message, svn::Depth depth, 1975 bool noIgnore, bool noUnknown) 1976 { 1977 if (!m_Data->m_CurrentContext) { 1978 return; 1979 } 1980 try { 1981 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 1982 i18nc("@title:window", "Import"), i18n("Importing items")); 1983 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 1984 m_Data->m_Svnclient->import(svn::Path(path), svn::Url(target), message, depth, noIgnore, noUnknown); 1985 } catch (const svn::Exception &e) { 1986 emit clientException(e.msg()); 1987 return; 1988 } 1989 } 1990 1991 void SvnActions::slotMergeExternal(const QString &_src1, const QString &_src2, const QString &_target, 1992 const svn::Revision &rev1, const svn::Revision &rev2, const svn::Revision &_peg, bool rec) 1993 { 1994 Q_UNUSED(_peg); 1995 QTemporaryDir tdir1; 1996 tdir1.setAutoRemove(true); 1997 QString src1 = _src1; 1998 QString src2 = _src2; 1999 QString target = _target; 2000 bool singleMerge = false; 2001 2002 if (rev1 == rev2 && (src2.isEmpty() || src1 == src2)) { 2003 singleMerge = true; 2004 } 2005 if (src1.isEmpty()) { 2006 emit clientException(i18n("Nothing to merge.")); 2007 return; 2008 } 2009 if (target.isEmpty()) { 2010 emit clientException(i18n("No destination to merge.")); 2011 return; 2012 } 2013 2014 QFileInfo f1(src1); 2015 QFileInfo f2(src2); 2016 bool isDir = true; 2017 2018 svn::InfoEntry i1, i2; 2019 2020 if (!singleInfo(src1, rev1, i1)) { 2021 return; 2022 } 2023 isDir = i1.isDir(); 2024 if (!singleMerge && src1 != src2) { 2025 if (!singleInfo(src2, rev2, i2)) { 2026 return; 2027 } 2028 if (i2.isDir() != isDir) { 2029 emit clientException(i18n("Both sources must be same type.")); 2030 return; 2031 } 2032 } 2033 2034 QFileInfo ti(target); 2035 if (ti.isDir() != isDir) { 2036 emit clientException(i18n("Target for merge must same type like sources.")); 2037 return; 2038 } 2039 2040 QString s1 = f1.fileName() + QLatin1Char('-') + rev1.toString(); 2041 QString s2 = f2.fileName() + QLatin1Char('-') + rev2.toString(); 2042 QString first, second; 2043 if (rev1 != svn::Revision::WORKING) { 2044 first = tdir1.path() + QLatin1Char('/') + s1; 2045 } else { 2046 first = src1; 2047 } 2048 if (!singleMerge) { 2049 if (rev2 != svn::Revision::WORKING) { 2050 second = tdir1.path() + QLatin1Char('/') + s2; 2051 } else { 2052 second = src2; 2053 } 2054 } else { 2055 // only two-way merge 2056 second.clear(); 2057 } 2058 if (second == first) { 2059 KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("Both entries seems to be the same, will not do a merge.")); 2060 return; 2061 } 2062 2063 if (rev1 != svn::Revision::WORKING) { 2064 if (isDir) { 2065 if (!makeCheckout(src1, first, rev1, svn::Revision::UNDEFINED, 2066 rec ? svn::DepthInfinity : svn::DepthFiles, 2067 true, false, false, false, false, nullptr)) { 2068 return; 2069 } 2070 } else { 2071 if (!get(src1, first, rev1, svn::Revision::UNDEFINED, m_Data->m_ParentList->realWidget())) { 2072 return; 2073 } 2074 } 2075 } 2076 2077 if (!singleMerge) { 2078 if (rev2 != svn::Revision::WORKING) { 2079 if (isDir) { 2080 if (!makeCheckout(src2, second, rev2, svn::Revision::UNDEFINED, 2081 rec ? svn::DepthInfinity : svn::DepthFiles, 2082 true, false, false, false, false, nullptr)) { 2083 return; 2084 } 2085 } else { 2086 if (!get(src2, second, rev2, svn::Revision::UNDEFINED, m_Data->m_ParentList->realWidget())) { 2087 return; 2088 } 2089 } 2090 } 2091 } 2092 const QString edisp = Kdesvnsettings::external_merge_program(); 2093 const QVector<QStringRef> wlist = edisp.splitRef(QLatin1Char(' ')); 2094 WatchedProcess *proc = new WatchedProcess(this); 2095 for (auto it = wlist.begin(); it != wlist.end(); ++it) { 2096 if (*it == QLatin1String("%s1")) { 2097 *proc << first; 2098 } else if (*it == QLatin1String("%s2")) { 2099 if (!second.isEmpty()) { 2100 *proc << second; 2101 } 2102 } else if (*it == QLatin1String("%t")) { 2103 *proc << target; 2104 } else { 2105 *proc << (*it).toString(); 2106 } 2107 } 2108 tdir1.setAutoRemove(false); 2109 proc->setAutoDelete(true); 2110 proc->appendTempDir(tdir1.path()); 2111 proc->setOutputChannelMode(KProcess::MergedChannels); 2112 connect(proc, &WatchedProcess::dataStderrRead, 2113 this, &SvnActions::slotProcessDataRead); 2114 connect(proc, &WatchedProcess::dataStdoutRead, 2115 this, &SvnActions::slotProcessDataRead); 2116 2117 proc->start(); 2118 if (proc->waitForStarted(-1)) { 2119 if (m_Data->runblocked) { 2120 proc->waitForFinished(-1); 2121 } 2122 } else { 2123 emit sendNotify(i18n("Merge process could not started, check command.")); 2124 } 2125 } 2126 2127 void SvnActions::slotMergeWcRevisions(const QString &_entry, const svn::Revision &rev1, 2128 const svn::Revision &rev2, 2129 bool rec, bool ancestry, bool forceIt, bool dry, bool allow_mixed_rev) 2130 { 2131 slotMerge(_entry, _entry, _entry, rev1, rev2, svn::Revision::UNDEFINED, rec, ancestry, forceIt, dry, false, false, allow_mixed_rev); 2132 } 2133 2134 void SvnActions::slotMerge(const QString &src1, const QString &src2, const QString &target, 2135 const svn::Revision &rev1, const svn::Revision &rev2, const svn::Revision &_peg, 2136 bool rec, bool ancestry, bool forceIt, bool dry, bool recordOnly, bool reintegrate, bool allow_mixed_rev) 2137 { 2138 Q_UNUSED(_peg); 2139 if (!m_Data->m_CurrentContext) { 2140 return; 2141 } 2142 2143 svn::Revision peg = svn::Revision::HEAD; 2144 svn::Revision tpeg; 2145 svn::RevisionRanges ranges; 2146 svn::Path p1; 2147 try { 2148 svn::Path::parsePeg(src1, p1, tpeg); 2149 } catch (const svn::Exception &e) { 2150 emit clientException(e.msg()); 2151 return; 2152 } 2153 if (tpeg != svn::Revision::UNDEFINED) { 2154 peg = tpeg; 2155 } 2156 svn::Path p2(src2); 2157 2158 bool pegged_merge = false; 2159 2160 // build merge Parameters 2161 svn::MergeParameter _merge_parameter; 2162 ranges.append(svn::RevisionRange(rev1, rev2)); 2163 _merge_parameter.revisions(ranges).path1(p1).path2(p2).depth(rec ? svn::DepthInfinity : svn::DepthFiles).notice_ancestry(ancestry).force(forceIt) 2164 .dry_run(dry).record_only(recordOnly).reintegrate(reintegrate).allow_mixed_rev(allow_mixed_rev) 2165 .localPath(svn::Path(target)).merge_options(svn::StringArray()); 2166 2167 if (!reintegrate && (!p2.isSet() || src1 == src2)) { 2168 // pegged merge 2169 pegged_merge = true; 2170 if (peg == svn::Revision::UNDEFINED) { 2171 if (p1.isUrl()) { 2172 peg = rev2; 2173 } else { 2174 peg = svn::Revision::WORKING; 2175 } 2176 } 2177 _merge_parameter.peg(peg); 2178 } 2179 2180 try { 2181 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 2182 i18nc("@title:window", "Merge"), i18n("Merging items")); 2183 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 2184 if (pegged_merge) { 2185 m_Data->m_Svnclient->merge_peg(_merge_parameter); 2186 } else { 2187 m_Data->m_Svnclient->merge(_merge_parameter); 2188 } 2189 } catch (const svn::Exception &e) { 2190 emit clientException(e.msg()); 2191 return; 2192 } 2193 m_Data->clearCaches(); 2194 } 2195 2196 /*! 2197 \fn SvnActions::slotCopyMove(bool,const QString&,const QString&) 2198 */ 2199 bool SvnActions::makeMove(const QString &Old, const QString &New) 2200 { 2201 if (!m_Data->m_CurrentContext) { 2202 return false; 2203 } 2204 svn::CopyParameter params(Old, New); 2205 svn::Revision nnum; 2206 2207 try { 2208 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 2209 i18nc("@title:window", "Move"), i18n("Moving/Rename item")); 2210 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 2211 nnum = m_Data->m_Svnclient->move(params.asChild(false).makeParent(false)); 2212 } catch (const svn::Exception &e) { 2213 emit clientException(e.msg()); 2214 return false; 2215 } 2216 if (nnum != svn::Revision::UNDEFINED) { 2217 emit sendNotify(i18n("Committed revision %1.", nnum.toString())); 2218 } 2219 EMIT_REFRESH; 2220 return true; 2221 } 2222 2223 bool SvnActions::makeMove(const QList<QUrl> &Old, const QString &New) 2224 { 2225 try { 2226 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 2227 i18nc("@title:window", "Move"), i18n("Moving entries")); 2228 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 2229 const svn::Path pNew(New); 2230 // either both are local paths -> move in wc, or both are urls -> move in repository 2231 const svn::Targets t(svn::Targets::fromUrlList(Old, pNew.isUrl() ? svn::Targets::UrlConversion::KeepUrl : svn::Targets::UrlConversion::PreferLocalPath)); 2232 m_Data->m_Svnclient->move(svn::CopyParameter(t, pNew).asChild(true).makeParent(false)); 2233 } catch (const svn::Exception &e) { 2234 emit clientException(e.msg()); 2235 return false; 2236 } 2237 return true; 2238 } 2239 2240 bool SvnActions::makeCopy(const QString &Old, const QString &New, const svn::Revision &rev) 2241 { 2242 if (!m_Data->m_CurrentContext) { 2243 return false; 2244 } 2245 try { 2246 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 2247 i18nc("@title:window", "Copy / Move"), i18n("Copy or Moving entries")); 2248 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 2249 m_Data->m_Svnclient->copy(svn::Path(Old), rev, svn::Path(New)); 2250 } catch (const svn::Exception &e) { 2251 emit clientException(e.msg()); 2252 return false; 2253 } 2254 EMIT_REFRESH; 2255 return true; 2256 } 2257 2258 bool SvnActions::makeCopy(const QList<QUrl> &Old, const QString &New, const svn::Revision &rev) 2259 { 2260 try { 2261 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 2262 i18nc("@title:window", "Copy / Move"), i18n("Copy or Moving entries")); 2263 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 2264 const svn::Path pNew(New); 2265 // either both are local paths -> copy in wc, or both are urls -> copy in repository 2266 const svn::Targets t(svn::Targets::fromUrlList(Old, pNew.isUrl() ? svn::Targets::UrlConversion::KeepUrl : svn::Targets::UrlConversion::PreferLocalPath)); 2267 m_Data->m_Svnclient->copy(svn::CopyParameter(t, pNew).srcRevision(rev).pegRevision(rev).asChild(true)); 2268 } catch (const svn::Exception &e) { 2269 emit clientException(e.msg()); 2270 return false; 2271 } 2272 return true; 2273 } 2274 2275 /*! 2276 \fn SvnActions::makeLock(const QStringList&) 2277 */ 2278 void SvnActions::makeLock(const QStringList &what, const QString &_msg, bool breakit) 2279 { 2280 if (!m_Data->m_CurrentContext) { 2281 return; 2282 } 2283 try { 2284 m_Data->m_Svnclient->lock(svn::Targets::fromStringList(what), _msg, breakit); 2285 } catch (const svn::Exception &e) { 2286 emit clientException(e.msg()); 2287 return; 2288 } 2289 } 2290 2291 2292 /*! 2293 \fn SvnActions::makeUnlock(const QStringList&) 2294 */ 2295 void SvnActions::makeUnlock(const QStringList &what, bool breakit) 2296 { 2297 if (!m_Data->m_CurrentContext) { 2298 return; 2299 } 2300 try { 2301 m_Data->m_Svnclient->unlock(svn::Targets::fromStringList(what), breakit); 2302 } catch (const svn::Exception &e) { 2303 emit clientException(e.msg()); 2304 return; 2305 } 2306 for (const QString &key : what) { 2307 m_Data->m_repoLockCache.deleteKey(key, true); 2308 } 2309 // m_Data->m_repoLockCache.dump_tree(); 2310 } 2311 2312 2313 /*! 2314 \fn SvnActions::makeStatus(const QString&what, svn::StatusEntries&dlist) 2315 */ 2316 bool SvnActions::makeStatus(const QString &what, svn::StatusEntries &dlist, const svn::Revision &where, bool rec, bool all) 2317 { 2318 bool display_ignores = Kdesvnsettings::display_ignored_files(); 2319 return makeStatus(what, dlist, where, rec, all, display_ignores); 2320 } 2321 2322 bool SvnActions::makeStatus(const QString &what, svn::StatusEntries &dlist, const svn::Revision &where, bool rec, bool all, bool display_ignores, bool updates) 2323 { 2324 svn::Depth _d = rec ? svn::DepthInfinity : svn::DepthImmediates; 2325 return makeStatus(what, dlist, where, _d, all, display_ignores, updates); 2326 } 2327 2328 bool SvnActions::makeStatus(const QString &what, svn::StatusEntries &dlist, const svn::Revision &where, svn::Depth _d, bool all, bool display_ignores, bool updates) 2329 { 2330 bool disp_remote_details = Kdesvnsettings::details_on_remote_listing(); 2331 try { 2332 #ifdef DEBUG_TIMER 2333 QTime _counttime; 2334 _counttime.start(); 2335 #endif 2336 svn::StatusParameter params(what); 2337 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), 2338 i18nc("@title:window", "Status / List"), i18n("Creating list / check status")); 2339 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); 2340 // rec all up noign 2341 dlist = m_Data->m_Svnclient->status(params.depth(_d).all(all).update(updates).noIgnore(display_ignores).revision(where).detailedRemote(disp_remote_details).ignoreExternals(false)); 2342 #ifdef DEBUG_TIMER 2343 qCDebug(KDESVN_LOG) << "Time for getting status: " << _counttime.elapsed(); 2344 #endif 2345 2346 } catch (const svn::Exception &e) { 2347 emit clientException(e.msg()); 2348 return false; 2349 } 2350 return true; 2351 } 2352 2353 void SvnActions::checkAddItems(const QString &path, bool print_error_box) 2354 { 2355 svn::StatusEntries dlist; 2356 svn::StatusEntries rlist; 2357 QStringList displist; 2358 svn::Revision where = svn::Revision::HEAD; 2359 if (!makeStatus(path, dlist, where, true, true, false, false)) { 2360 return; 2361 } 2362 for (const auto &entry : qAsConst(dlist)) { 2363 if (!entry->isVersioned()) { 2364 rlist.append(entry); 2365 displist.append(entry->path()); 2366 } 2367 } 2368 if (rlist.isEmpty()) { 2369 if (print_error_box) { 2370 KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("No unversioned items found.")); 2371 } 2372 } else { 2373 QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("add_items_dlg"))); 2374 dlg->setWindowTitle(i18nc("@title:window", "Add Unversioned Items")); 2375 dlg->setWithCancelButton(); 2376 QTreeWidget *ptr(new QTreeWidget(dlg)); 2377 ptr->headerItem()->setText(0, i18n("Item")); 2378 for (int j = 0; j < displist.size(); ++j) { 2379 QTreeWidgetItem *n = new QTreeWidgetItem(ptr); 2380 n->setText(0, displist[j]); 2381 n->setCheckState(0, Qt::Checked); 2382 } 2383 ptr->resizeColumnToContents(0); 2384 dlg->addWidget(ptr); 2385 if (dlg->exec() == QDialog::Accepted) { 2386 QTreeWidgetItemIterator it(ptr); 2387 displist.clear(); 2388 while (*it) { 2389 QTreeWidgetItem *t = (*it); 2390 if (t->checkState(0) == Qt::Checked) { 2391 displist.append(t->text(0)); 2392 } 2393 ++it; 2394 } 2395 if (!displist.isEmpty()) { 2396 addItems(svn::Targets::fromStringList(displist).targets(), svn::DepthEmpty); 2397 } 2398 } 2399 delete dlg; 2400 } 2401 } 2402 2403 void SvnActions::stopCheckModifiedThread() 2404 { 2405 if (m_CThread) { 2406 m_CThread->cancelMe(); 2407 if (!m_CThread->wait(MAX_THREAD_WAITTIME)) { 2408 m_CThread->terminate(); 2409 m_CThread->wait(MAX_THREAD_WAITTIME); 2410 } 2411 delete m_CThread; 2412 m_CThread = nullptr; 2413 } 2414 } 2415 2416 void SvnActions::stopCheckUpdateThread() 2417 { 2418 if (m_UThread) { 2419 m_UThread->cancelMe(); 2420 if (!m_UThread->wait(MAX_THREAD_WAITTIME)) { 2421 m_UThread->terminate(); 2422 m_UThread->wait(MAX_THREAD_WAITTIME); 2423 } 2424 delete m_UThread; 2425 m_UThread = nullptr; 2426 } 2427 } 2428 2429 void SvnActions::stopFillCache() 2430 { 2431 if (m_FCThread) { 2432 m_FCThread->cancelMe(); 2433 if (!m_FCThread->wait(MAX_THREAD_WAITTIME)) { 2434 m_FCThread->terminate(); 2435 m_FCThread->wait(MAX_THREAD_WAITTIME); 2436 } 2437 delete m_FCThread; 2438 m_FCThread = nullptr; 2439 emit sigThreadsChanged(); 2440 emit sigCacheStatus(-1, -1); 2441 } 2442 } 2443 2444 void SvnActions::stopMain() 2445 { 2446 if (m_Data->m_CurrentContext) { 2447 m_Data->m_SvnContextListener->setCanceled(true); 2448 sleep(1); 2449 m_Data->m_SvnContextListener->contextCancel(); 2450 } 2451 } 2452 2453 void SvnActions::killallThreads() 2454 { 2455 stopMain(); 2456 stopCheckModifiedThread(); 2457 stopCheckUpdateThread(); 2458 stopFillCache(); 2459 } 2460 2461 bool SvnActions::createModifiedCache(const QString &what) 2462 { 2463 stopCheckModifiedThread(); 2464 m_CThread = new CheckModifiedThread(this, what, false); 2465 connect(m_CThread, &CheckModifiedThread::checkModifiedFinished, 2466 this, &SvnActions::checkModifiedThread); 2467 m_CThread->start(); 2468 return true; 2469 } 2470 2471 void SvnActions::checkModifiedThread() 2472 { 2473 if (!m_CThread) { 2474 return; 2475 } 2476 if (m_CThread->isRunning()) { 2477 QTimer::singleShot(2, this, &SvnActions::checkModifiedThread); 2478 return; 2479 } 2480 m_Data->m_Cache.clear(); 2481 m_Data->m_conflictCache.clear(); 2482 const svn::StatusEntries &sEntries = m_CThread->getList(); 2483 for (const auto &ptr : sEntries) { 2484 if (ptr->isRealVersioned() && ( 2485 ptr->nodeStatus() == svn_wc_status_modified || 2486 ptr->nodeStatus() == svn_wc_status_added || 2487 ptr->nodeStatus() == svn_wc_status_deleted || 2488 ptr->nodeStatus() == svn_wc_status_replaced || 2489 ptr->nodeStatus() == svn_wc_status_modified 2490 )) { 2491 m_Data->m_Cache.insertKey(ptr, ptr->path()); 2492 } else if (ptr->nodeStatus() == svn_wc_status_conflicted) { 2493 m_Data->m_conflictCache.insertKey(ptr, ptr->path()); 2494 } 2495 emit sigRefreshItem(ptr->path()); 2496 } 2497 emit sigExtraStatusMessage(i18np("Found %1 modified item", "Found %1 modified items", sEntries.size())); 2498 delete m_CThread; 2499 m_CThread = nullptr; 2500 emit sigCacheDataChanged(); 2501 } 2502 2503 void SvnActions::checkUpdateThread() 2504 { 2505 if (!m_UThread || m_UThread->isRunning()) { 2506 if (m_UThread) { 2507 QTimer::singleShot(2, this, &SvnActions::checkUpdateThread); 2508 } 2509 return; 2510 } 2511 bool newer = false; 2512 const svn::StatusEntries &sEntries = m_UThread->getList(); 2513 for (const auto &ptr : sEntries) { 2514 if (ptr->validReposStatus()) { 2515 m_Data->m_UpdateCache.insertKey(ptr, ptr->path()); 2516 if (!(ptr->validLocalStatus())) { 2517 newer = true; 2518 } 2519 } 2520 if (ptr->isLocked() && 2521 !(ptr->entry().lockEntry().Locked())) { 2522 m_Data->m_repoLockCache.insertKey(ptr, ptr->path()); 2523 } 2524 emit sigRefreshItem(ptr->path()); 2525 } 2526 emit sigExtraStatusMessage(i18n("Checking for updates finished")); 2527 if (newer) { 2528 emit sigExtraStatusMessage(i18n("There are new items in repository")); 2529 } 2530 delete m_UThread; 2531 m_UThread = nullptr; 2532 emit sigCacheDataChanged(); 2533 } 2534 2535 void SvnActions::getaddedItems(const QString &path, svn::StatusEntries &target) 2536 { 2537 helpers::ValidRemoteOnly vro; 2538 m_Data->m_UpdateCache.listsubs_if(path, vro); 2539 target = vro.liste(); 2540 } 2541 2542 bool SvnActions::checkUpdatesRunning() 2543 { 2544 return m_UThread && m_UThread->isRunning(); 2545 } 2546 2547 void SvnActions::addModifiedCache(const svn::StatusPtr &what) 2548 { 2549 if (what->nodeStatus() == svn_wc_status_conflicted) { 2550 m_Data->m_conflictCache.insertKey(what, what->path()); 2551 emit sigRefreshItem(what->path()); 2552 } else { 2553 m_Data->m_Cache.insertKey(what, what->path()); 2554 } 2555 } 2556 2557 void SvnActions::deleteFromModifiedCache(const QString &what) 2558 { 2559 m_Data->m_Cache.deleteKey(what, true); 2560 m_Data->m_conflictCache.deleteKey(what, true); 2561 //m_Data->m_Cache.dump_tree(); 2562 emit sigRefreshItem(what); 2563 } 2564 2565 bool SvnActions::checkModifiedCache(const QString &path) const 2566 { 2567 return m_Data->m_Cache.find(path); 2568 } 2569 2570 bool SvnActions::checkReposLockCache(const QString &path) const 2571 { 2572 return m_Data->m_repoLockCache.findSingleValid(path, false); 2573 } 2574 2575 bool SvnActions::checkReposLockCache(const QString &path, svn::StatusPtr &t) const 2576 { 2577 /// @todo create a method where svn::Status* will be a parameter so no copy is needed but just reading content 2578 return m_Data->m_repoLockCache.findSingleValid(path, t); 2579 } 2580 2581 bool SvnActions::checkConflictedCache(const QString &path) const 2582 { 2583 return m_Data->m_conflictCache.find(path); 2584 } 2585 2586 void SvnActions::startFillCache(const QString &path, bool startup) 2587 { 2588 #ifdef DEBUG_TIMER 2589 QTime _counttime; 2590 _counttime.start(); 2591 #endif 2592 stopFillCache(); 2593 #ifdef DEBUG_TIMER 2594 qCDebug(KDESVN_LOG) << "Stopped cache " << _counttime.elapsed(); 2595 _counttime.restart(); 2596 #endif 2597 if (!doNetworking()) { 2598 emit sendNotify(i18n("Not filling log cache because networking is disabled")); 2599 return; 2600 } 2601 2602 m_FCThread = new FillCacheThread(this, path, startup); 2603 connect(m_FCThread, &FillCacheThread::fillCacheStatus, 2604 this, &SvnActions::sigCacheStatus); 2605 connect(m_FCThread, &FillCacheThread::fillCacheFinished, 2606 this, &SvnActions::stopFillCache); 2607 m_FCThread->start(); 2608 } 2609 2610 bool SvnActions::doNetworking() 2611 { 2612 // if networking is allowd we don't need extra checks, second is just for avoiding segfaults 2613 if (Kdesvnsettings::network_on() || !m_Data->m_ParentList) { 2614 return true; 2615 } 2616 bool is_url = false; 2617 if (m_Data->m_ParentList->isNetworked()) { 2618 // if called http:// etc.pp. 2619 is_url = true; 2620 } else if (m_Data->m_ParentList->baseUri().startsWith(QLatin1Char('/'))) { 2621 // if opened a working copy we must check if it points to a networking repository 2622 svn::InfoEntry e; 2623 if (!singleInfo(m_Data->m_ParentList->baseUri(), svn::Revision::UNDEFINED, e)) { 2624 return false; 2625 } 2626 is_url = !e.reposRoot().isLocalFile(); 2627 } 2628 return !is_url; 2629 } 2630 2631 /*! 2632 \fn SvnActions::createUpdateCache(const QString&what) 2633 */ 2634 bool SvnActions::createUpdateCache(const QString &what) 2635 { 2636 clearUpdateCache(); 2637 m_Data->m_repoLockCache.clear(); 2638 stopCheckUpdateThread(); 2639 if (!doNetworking()) { 2640 emit sigExtraStatusMessage(i18n("Not checking for updates because networking is disabled")); 2641 return false; 2642 } 2643 m_UThread = new CheckModifiedThread(this, what, true); 2644 connect(m_UThread, &CheckModifiedThread::checkModifiedFinished, 2645 this, &SvnActions::checkUpdateThread); 2646 m_UThread->start(); 2647 emit sigExtraStatusMessage(i18n("Checking for updates started in background")); 2648 return true; 2649 } 2650 2651 bool SvnActions::checkUpdateCache(const QString &path)const 2652 { 2653 return m_Data->m_UpdateCache.find(path); 2654 } 2655 2656 void SvnActions::removeFromUpdateCache(const QStringList &what, bool exact_only) 2657 { 2658 for (int i = 0; i < what.size(); ++i) { 2659 m_Data->m_UpdateCache.deleteKey(what.at(i), exact_only); 2660 } 2661 } 2662 2663 bool SvnActions::isUpdated(const QString &path)const 2664 { 2665 svn::StatusPtr d; 2666 return getUpdated(path, d); 2667 } 2668 2669 bool SvnActions::getUpdated(const QString &path, svn::StatusPtr &d)const 2670 { 2671 return m_Data->m_UpdateCache.findSingleValid(path, d); 2672 } 2673 2674 void SvnActions::clearUpdateCache() 2675 { 2676 m_Data->m_UpdateCache.clear(); 2677 } 2678 2679 bool SvnActions::makeIgnoreEntry(const svn::Path &item, const QStringList &ignorePattern, bool unignore) 2680 { 2681 svn::Revision r(svn::Revision::UNDEFINED); 2682 2683 QPair<qlonglong, svn::PathPropertiesMapList> pmp; 2684 try { 2685 pmp = m_Data->m_Svnclient->propget(QStringLiteral("svn:ignore"), item, r, r); 2686 } catch (const svn::Exception &e) { 2687 emit clientException(e.msg()); 2688 return false; 2689 } 2690 svn::PathPropertiesMapList pm = pmp.second; 2691 QString data; 2692 if (!pm.isEmpty()) { 2693 const svn::PropertiesMap &mp = pm[0].second; 2694 data = mp[QStringLiteral("svn:ignore")]; 2695 } 2696 bool result = false; 2697 QStringList lst = data.split(QLatin1Char('\n'), QString::SkipEmptyParts); 2698 2699 for (int _current = 0; _current < ignorePattern.size(); ++_current) { 2700 int it = lst.indexOf(ignorePattern[_current]); 2701 if (it != -1) { 2702 if (unignore) { 2703 lst.removeAt(it); 2704 result = true; 2705 } 2706 } else { 2707 if (!unignore) { 2708 lst.append(ignorePattern[_current]); 2709 result = true; 2710 } 2711 } 2712 } 2713 if (result) { 2714 data = lst.join(QLatin1Char('\n')); 2715 try { 2716 m_Data->m_Svnclient->propset(svn::PropertiesParameter().propertyName(QStringLiteral("svn:ignore")).propertyValue(data).path(item)); 2717 } catch (const svn::Exception &e) { 2718 emit clientException(e.msg()); 2719 return false; 2720 } 2721 } 2722 return result; 2723 } 2724 2725 bool SvnActions::makeIgnoreEntry(SvnItem *which, bool unignore) 2726 { 2727 if (!which) { 2728 return false; 2729 } 2730 QString parentName = which->getParentDir(); 2731 if (parentName.isEmpty()) { 2732 return false; 2733 } 2734 QString name = which->shortName(); 2735 return makeIgnoreEntry(svn::Path(parentName), QStringList(name), unignore); 2736 } 2737 2738 svn::PathPropertiesMapListPtr SvnActions::propList(const QString &which, const svn::Revision &where, bool cacheOnly) 2739 { 2740 svn::PathPropertiesMapListPtr pm; 2741 if (!which.isEmpty()) { 2742 QString fk = where.toString() + QLatin1Char('/') + which; 2743 svn::Path p(which); 2744 2745 if (where != svn::Revision::WORKING) { 2746 m_Data->m_PropertiesCache.findSingleValid(fk, pm); 2747 } 2748 if (!pm && !cacheOnly) { 2749 try { 2750 pm = m_Data->m_Svnclient->proplist(p, where, where); 2751 } catch (const svn::Exception &e) { 2752 /* no messagebox needed */ 2753 if (e.apr_err() != SVN_ERR_WC_NOT_DIRECTORY) { 2754 emit sendNotify(e.msg()); 2755 } 2756 } 2757 if (where != svn::Revision::WORKING && pm) { 2758 m_Data->m_PropertiesCache.insertKey(pm, fk); 2759 } 2760 } 2761 } 2762 return pm; 2763 } 2764 2765 bool SvnActions::isLockNeeded(SvnItem *which, const svn::Revision &where) 2766 { 2767 if (!which) { 2768 return false; 2769 } 2770 svn::Path p(which->fullName()); 2771 2772 QPair<qlonglong, svn::PathPropertiesMapList> pmp; 2773 try { 2774 pmp = m_Data->m_Svnclient->propget(QStringLiteral("svn:needs-lock"), p, where, where); 2775 } catch (const svn::Exception &e) { 2776 /* no messagebox needed */ 2777 //emit clientException(e.msg()); 2778 return false; 2779 } 2780 const svn::PathPropertiesMapList pm = pmp.second; 2781 if (!pm.isEmpty()) { 2782 const svn::PropertiesMap &mp = pm.at(0).second; 2783 if (mp.contains(QStringLiteral("svn:needs-lock"))) { 2784 return true; 2785 } 2786 } 2787 return false; 2788 } 2789 2790 QString SvnActions::searchProperty(QString &Store, const QString &property, const QString &start, const svn::Revision &where, bool up) 2791 { 2792 svn::Path pa(start); 2793 svn::InfoEntry inf; 2794 2795 if (!singleInfo(start, where, inf)) { 2796 return QString(); 2797 } 2798 while (pa.length() > 0) { 2799 const svn::PathPropertiesMapListPtr pm = propList(pa.path(), where, false); 2800 if (!pm) { 2801 return QString(); 2802 } 2803 if (!pm->isEmpty()) { 2804 const svn::PropertiesMap &mp = pm->at(0).second; 2805 const svn::PropertiesMap::ConstIterator it = mp.find(property); 2806 if (it != mp.end()) { 2807 Store = *it; 2808 return pa.path(); 2809 } 2810 } 2811 if (up) { 2812 pa.removeLast(); 2813 if (pa.isUrl() && inf.reposRoot().toString().length() > pa.path().length()) { 2814 break; 2815 } 2816 2817 } else { 2818 break; 2819 } 2820 } 2821 return QString(); 2822 } 2823 2824 bool SvnActions::makeList(const QString &url, svn::DirEntries &dlist, const svn::Revision &where, svn::Depth depth) 2825 { 2826 if (!m_Data->m_CurrentContext) { 2827 return false; 2828 } 2829 try { 2830 dlist = m_Data->m_Svnclient->list(url, where, where, depth, false); 2831 } catch (const svn::Exception &e) { 2832 qCDebug(KDESVN_LOG) << "List fehler: " << e.msg(); 2833 emit clientException(e.msg()); 2834 return false; 2835 } 2836 return true; 2837 } 2838 2839 bool SvnActions::isLocalWorkingCopy(const QString &path, QUrl &repoUrl) 2840 { 2841 if (path.isEmpty()) { 2842 return false; 2843 } 2844 const QUrl url = helpers::KTranslateUrl::string2Uri(path); 2845 if (!url.isLocalFile()) { 2846 qCDebug(KDESVN_LOG) << "isLocalWorkingCopy no local file: " << path << " - " << url.toString(); 2847 return false; 2848 } 2849 2850 QString cleanpath = url.adjusted(QUrl::StripTrailingSlash|QUrl::NormalizePathSegments).path(); 2851 qCDebug(KDESVN_LOG) << "isLocalWorkingCopy for " << cleanpath; 2852 repoUrl.clear(); 2853 svn::Revision peg(svn_opt_revision_unspecified); 2854 svn::Revision rev(svn_opt_revision_unspecified); 2855 svn::InfoEntries e; 2856 try { 2857 e = m_Data->m_Svnclient->info(cleanpath, svn::DepthEmpty, rev, peg); 2858 } catch (const svn::Exception &e) { 2859 2860 if (SVN_ERR_WC_NOT_DIRECTORY == e.apr_err()) { 2861 return false; 2862 } 2863 return true; 2864 } 2865 if (!e.isEmpty()) 2866 repoUrl = e.at(0).url(); 2867 return true; 2868 } 2869 2870 void SvnActions::slotExtraLogMsg(const QString &msg) 2871 { 2872 emit sigExtraLogMsg(msg); 2873 } 2874 2875 void SvnActions::slotCancel(bool how) 2876 { 2877 if (!m_Data->m_CurrentContext) { 2878 return; 2879 } 2880 m_Data->m_SvnContextListener->setCanceled(how); 2881 } 2882 2883 void SvnActions::setContextData(const QString &aKey, const QString &aValue) 2884 { 2885 if (aValue.isNull()) { 2886 QMap<QString, QString>::iterator it = m_Data->m_contextData.find(aKey); 2887 if (it != m_Data->m_contextData.end()) { 2888 m_Data->m_contextData.remove(aKey); 2889 } 2890 } else { 2891 m_Data->m_contextData[aKey] = aValue; 2892 } 2893 } 2894 2895 void SvnActions::clearContextData() 2896 { 2897 m_Data->m_contextData.clear(); 2898 } 2899 2900 QString SvnActions::getContextData(const QString &aKey)const 2901 { 2902 if (m_Data->m_contextData.find(aKey) != m_Data->m_contextData.end()) { 2903 return m_Data->m_contextData[aKey]; 2904 } 2905 return QString(); 2906 } 2907 2908 bool SvnActions::threadRunning(ThreadType which) const 2909 { 2910 switch (which) { 2911 case checkupdatethread: 2912 return (m_UThread && m_UThread->isRunning()); 2913 case fillcachethread: 2914 return (m_FCThread && m_FCThread->isRunning()); 2915 case checkmodifiedthread: 2916 return (m_CThread && m_CThread->isRunning()); 2917 } 2918 return false; 2919 }