1 /***************************************************************************
2 * Copyright (C) 2005-2009 by Rajko Albrecht *
3 * ral@alwins-world.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20 #include "svnactions.h"
21 #include "checkoutinfo_impl.h"
22 #include "itemdisplay.h"
23 #include "svnitem.h"
24 #include "rangeinput_impl.h"
25 #include "propertiesdlg.h"
26 #include "ccontextlistener.h"
27 #include "tcontextlistener.h"
28 #include "modifiedthread.h"
29 #include "fillcachethread.h"
30 #include "svnlogdlgimp.h"
31 #include "stopdlg.h"
32 #include "blamedisplay.h"
33 #include "ksvnwidgets/commitmsg_impl.h"
34 #include "ksvnwidgets/models/commitmodelhelper.h"
35 #include "ksvnwidgets/diffbrowser.h"
36 #include "ksvnwidgets/encodingselector_impl.h"
37 #include "ksvnwidgets/revertform.h"
38 #include "graphtree/revisiontree.h"
39 #include "graphtree/revtreewidget.h"
40 #include "settings/kdesvnsettings.h"
41 #include "svnqt/client.h"
42 #include "svnqt/annotate_line.h"
43 #include "svnqt/context_listener.h"
44 #include "svnqt/dirent.h"
45 #include "svnqt/targets.h"
46 #include "svnqt/url.h"
47 #include "svnqt/svnqttypes.h"
48 #include "svnqt/svnqt_defines.h"
49 #include "svnqt/client_parameter.h"
50 #include "svnqt/client_commit_parameter.h"
51 #include "svnqt/client_annotate_parameter.h"
52 #include "svnqt/client_update_parameter.h"
53 #include "svnqt/cache/LogCache.h"
54 #include "svnqt/cache/ReposLog.h"
55 #include "svnqt/cache/ReposConfig.h"
56
57 #include "fronthelpers/watchedprocess.h"
58
59 #include "helpers/stringhelper.h"
60 #include "helpers/kdesvn_debug.h"
61 #include "helpers/ktranslateurl.h"
62 #include "helpers/windowgeometryhelper.h"
63 #include "fronthelpers/cursorstack.h"
64 #include "cacheentry.h"
65
66 #include <klocalizedstring.h>
67 #include <kmessagebox.h>
68 #include <kconfig.h>
69 #include <kmimetypetrader.h>
70 #include <krun.h>
71
72 #include <QApplication>
73 #include <QDesktopServices>
74 #include <QFileInfo>
75 #include <QFontDatabase>
76 #include <QInputDialog>
77 #include <QMap>
78 #include <QMimeDatabase>
79 #include <QReadWriteLock>
80 #include <QString>
81 #include <QTimer>
82 #include <QTemporaryDir>
83 #include <QTemporaryFile>
84 #include <QTreeWidget>
85 #include <QTreeWidgetItem>
86
87 #include <sys/time.h>
88 #include <unistd.h>
89
90 /// @todo has to be removed for a real fix of ticket #613
91 #include <svn_config.h>
92 // wait not longer than 10 seconds for a thread
93 #define MAX_THREAD_WAITTIME 10000
94
95 class SvnActionsData
96 {
97 public:
SvnActionsData()98 SvnActionsData()
99 : m_ParentList(nullptr)
100 , m_SvnContextListener(nullptr)
101 , m_Svnclient(svn::Client::getobject(svn::ContextP()))
102 , runblocked(false)
103 {
104 }
105
~SvnActionsData()106 ~SvnActionsData()
107 {
108 cleanDialogs();
109 delete m_SvnContextListener;
110 }
111
isExternalDiff()112 static bool isExternalDiff()
113 {
114 if (Kdesvnsettings::use_external_diff()) {
115 const QString edisp = Kdesvnsettings::external_diff_display();
116 const QVector<QStringRef> wlist = edisp.splitRef(QLatin1Char(' '));
117 if (wlist.count() >= 3 && edisp.contains(QLatin1String("%1")) && edisp.contains(QLatin1String("%2"))) {
118 return true;
119 }
120 }
121 return false;
122 }
123
clearCaches()124 void clearCaches()
125 {
126 QWriteLocker wl(&(m_InfoCacheLock));
127 m_PropertiesCache.clear();
128 m_contextData.clear();
129 m_InfoCache.clear();
130 }
131
cleanDialogs()132 void cleanDialogs()
133 {
134 if (m_DiffDialog) {
135 delete m_DiffDialog;
136 m_DiffDialog = nullptr;
137 }
138 if (m_LogDialog) {
139 m_LogDialog->saveSize();
140 delete m_LogDialog;
141 m_LogDialog = nullptr;
142 }
143 }
144
145 /// @todo set some standards options to svn::Context. This should made via a Config class in svnqt (future release 1.4)
146 /// this is a workaround for ticket #613
setStandards()147 void setStandards()
148 {
149 if (!m_CurrentContext) {
150 return;
151 }
152 svn_config_t *cfg_config = static_cast<svn_config_t *>(apr_hash_get(m_CurrentContext->ctx()->config, SVN_CONFIG_CATEGORY_CONFIG,
153 APR_HASH_KEY_STRING));
154 if (!cfg_config) {
155 return;
156 }
157 svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
158 SVN_CONFIG_OPTION_DIFF_CMD, nullptr);
159 }
160
161 ItemDisplay *m_ParentList;
162
163 CContextListener *m_SvnContextListener;
164 svn::ContextP m_CurrentContext;
165 svn::ClientP m_Svnclient;
166
167 helpers::statusCache m_UpdateCache;
168 helpers::statusCache m_Cache;
169 helpers::statusCache m_conflictCache;
170 helpers::statusCache m_repoLockCache;
171 helpers::itemCache<svn::PathPropertiesMapListPtr> m_PropertiesCache;
172 /// \todo as persistent cache (sqlite?)
173 helpers::itemCache<svn::InfoEntry> m_InfoCache;
174 helpers::itemCache<QVariant> m_MergeInfoCache;
175
176 QPointer<DiffBrowser> m_DiffBrowserPtr;
177 QPointer<KSvnSimpleOkDialog> m_DiffDialog;
178 QPointer<SvnLogDlgImp> m_LogDialog;
179
180 QMap<QString, QString> m_contextData;
181 QReadWriteLock m_InfoCacheLock;
182
183 bool runblocked;
184 };
185
186 #define EMIT_FINISHED emit sendNotify(i18n("Finished"))
187 #define EMIT_REFRESH emit sigRefreshAll()
188 #define DIALOGS_SIZES "display_dialogs_sizes"
189
SvnActions(ItemDisplay * parent,bool processes_blocked)190 SvnActions::SvnActions(ItemDisplay *parent, bool processes_blocked)
191 : QObject(parent ? parent->realWidget() : nullptr)
192 , SimpleLogCb()
193 , m_CThread(nullptr)
194 , m_UThread(nullptr)
195 , m_FCThread(nullptr)
196 {
197 m_Data.reset(new SvnActionsData);
198 m_Data->m_ParentList = parent;
199 m_Data->m_SvnContextListener = new CContextListener(this);
200 m_Data->runblocked = processes_blocked;
201 connect(m_Data->m_SvnContextListener, &CContextListener::sendNotify,
202 this, &SvnActions::slotNotifyMessage);
203 }
204
svnclient()205 svn::ClientP SvnActions::svnclient()
206 {
207 return m_Data->m_Svnclient;
208 }
209
~SvnActions()210 SvnActions::~SvnActions()
211 {
212 killallThreads();
213 }
214
slotNotifyMessage(const QString & aMsg)215 void SvnActions::slotNotifyMessage(const QString &aMsg)
216 {
217 emit sendNotify(aMsg);
218 }
219
reInitClient()220 void SvnActions::reInitClient()
221 {
222 m_Data->clearCaches();
223 m_Data->cleanDialogs();
224 if (m_Data->m_CurrentContext) {
225 m_Data->m_CurrentContext->setListener(nullptr);
226 }
227 m_Data->m_CurrentContext = svn::ContextP(new svn::Context);
228 m_Data->m_CurrentContext->setListener(m_Data->m_SvnContextListener);
229 m_Data->m_Svnclient->setContext(m_Data->m_CurrentContext);
230 ///@todo workaround has to be replaced
231 m_Data->setStandards();
232 }
233
makeLog(const svn::Revision & start,const svn::Revision & end,const svn::Revision & peg,const QString & which,bool follow,bool list_files,int limit)234 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)
235 {
236 svn::LogEntriesMapPtr logs = getLog(start, end, peg, which, list_files, limit, follow);
237 if (!logs) {
238 return;
239 }
240 svn::InfoEntry info;
241 if (!singleInfo(which, peg, info)) {
242 return;
243 }
244 const QString reposRoot = info.reposRoot().toString();
245 bool need_modal = m_Data->runblocked || QApplication::activeModalWidget() != nullptr;
246 if (need_modal || !m_Data->m_LogDialog) {
247 m_Data->m_LogDialog = new SvnLogDlgImp(this, need_modal);
248 connect(m_Data->m_LogDialog, &SvnLogDlgImp::makeDiff,
249 this, QOverload<const QString&, const svn::Revision&, const QString&, const svn::Revision&, QWidget*>::of(&SvnActions::makeDiff));
250 connect(m_Data->m_LogDialog, &SvnLogDlgImp::makeCat,
251 this, &SvnActions::slotMakeCat);
252 }
253
254 if (m_Data->m_LogDialog) {
255 m_Data->m_LogDialog->dispLog(logs, info.url().toString().mid(reposRoot.length()), reposRoot,
256 (
257 peg == svn::Revision::UNDEFINED ?
258 (svn::Url::isValid(which) ? svn::Revision::HEAD : svn::Revision::UNDEFINED) :
259 peg
260 ), which);
261 if (need_modal) {
262 m_Data->m_LogDialog->exec();
263 m_Data->m_LogDialog->saveSize();
264 delete m_Data->m_LogDialog;
265 } else {
266 m_Data->m_LogDialog->show();
267 m_Data->m_LogDialog->raise();
268 }
269 }
270 EMIT_FINISHED;
271 }
272
getLog(const svn::Revision & start,const svn::Revision & end,const svn::Revision & peg,const QString & which,bool list_files,int limit,QWidget * parent)273 svn::LogEntriesMapPtr SvnActions::getLog(const svn::Revision &start, const svn::Revision &end, const svn::Revision &peg, const QString &which, bool list_files,
274 int limit, QWidget *parent)
275 {
276 return getLog(start, end, peg, which, list_files, limit, Kdesvnsettings::log_follows_nodes(), parent);
277 }
278
getLog(const svn::Revision & start,const svn::Revision & end,const svn::Revision & peg,const QString & which,bool list_files,int limit,bool follow,QWidget * parent)279 svn::LogEntriesMapPtr SvnActions::getLog(const svn::Revision &start, const svn::Revision &end, const svn::Revision &peg, const QString &which, bool list_files,
280 int limit, bool follow, QWidget *parent)
281 {
282 svn::LogEntriesMapPtr logs;
283 if (!m_Data->m_CurrentContext) {
284 return logs;
285 }
286
287 bool mergeinfo = hasMergeInfo(m_Data->m_ParentList->baseUri().isEmpty() ? which : m_Data->m_ParentList->baseUri());
288
289 svn::LogParameter params;
290 params.targets(which).revisionRange(start, end).peg(peg).includeMergedRevisions(mergeinfo).limit(limit).discoverChangedPathes(list_files).strictNodeHistory(!follow);
291
292 try {
293 StopDlg sdlg(m_Data->m_SvnContextListener, (parent ? parent : m_Data->m_ParentList->realWidget()),
294 i18nc("@title:window", "Logs"), i18n("Getting logs - hit Cancel for abort"));
295 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage);
296 logs = svn::LogEntriesMapPtr(new svn::LogEntriesMap);
297 if (doNetworking()) {
298 if (!m_Data->m_Svnclient->log(params, *logs)) {
299 logs.clear();
300 return logs;
301 }
302 } else {
303 svn::InfoEntry e;
304 if (!singleInfo(m_Data->m_ParentList->baseUri(), svn::Revision::BASE, e)) {
305 logs.clear();
306 return logs;
307 }
308 if (svn::Url::isLocal(e.reposRoot().toString())) {
309 if (!m_Data->m_Svnclient->log(params, *logs)) {
310 logs.clear();
311 return logs;
312 }
313 } else {
314 svn::cache::ReposLog rl(m_Data->m_Svnclient, e.reposRoot().toString());
315 QString what;
316 const QString s1 = e.url().toString().mid(e.reposRoot().toString().length());
317 if (which == QLatin1String(".")) {
318 what = s1;
319 } else {
320 const QString s2 = which.mid(m_Data->m_ParentList->baseUri().length());
321 what = s1 + QLatin1Char('/') + s2;
322 }
323 rl.log(what, start, end, peg, *logs, !follow, limit);
324 }
325 }
326 } catch (const svn::Exception &e) {
327 emit clientException(e.msg());
328 logs.clear();
329 }
330 if (logs && logs->isEmpty()) {
331 logs.clear();
332 emit clientException(i18n("Got no logs"));
333 }
334 return logs;
335 }
336
getSingleLog(svn::LogEntry & t,const svn::Revision & r,const QString & what,const svn::Revision & peg,QString & root)337 bool SvnActions::getSingleLog(svn::LogEntry &t, const svn::Revision &r, const QString &what, const svn::Revision &peg, QString &root)
338 {
339 bool res = false;
340
341 if (what.isEmpty()) {
342 return res;
343 }
344 if (root.isEmpty()) {
345 svn::InfoEntry inf;
346 if (!singleInfo(what, peg, inf)) {
347 return res;
348 }
349 root = inf.reposRoot().toString();
350 }
351
352 if (!svn::Url::isLocal(root)) {
353 svn::LogEntriesMap _m;
354 try {
355 svn::cache::ReposLog rl(m_Data->m_Svnclient , root);
356 if (rl.isValid() && rl.simpleLog(_m, r, r, true)) {
357 const svn::LogEntriesMap::const_iterator it = _m.constFind(r.revnum());
358 if (it != _m.constEnd()) {
359 t = it.value();
360 res = true;
361 }
362 }
363 } catch (const svn::Exception &e) {
364 emit clientException(e.msg());
365 }
366 }
367
368 if (!res) {
369 svn::LogEntriesMapPtr log = getLog(r, r, peg, root, true, 1);
370 if (log) {
371 const svn::LogEntriesMap::const_iterator it = log->constFind(r.revnum());
372 if (it != log->constEnd()) {
373 t = it.value();
374 res = true;
375 }
376 }
377 }
378 return res;
379 }
380
hasMergeInfo(const QString & originpath)381 bool SvnActions::hasMergeInfo(const QString &originpath)
382 {
383 QVariant _m(false);
384 QString path;
385
386 svn::InfoEntry e;
387 if (!singleInfo(originpath, svn::Revision::UNDEFINED, e)) {
388 return false;
389 }
390 path = e.reposRoot().toString();
391 if (!m_Data->m_MergeInfoCache.findSingleValid(path, _m)) {
392 bool mergeinfo;
393 try {
394 mergeinfo = m_Data->m_Svnclient->RepoHasCapability(path, svn::CapabilityMergeinfo);
395 } catch (const svn::ClientException &e) {
396 emit sendNotify(e.msg());
397 return false;
398 }
399 _m.setValue(mergeinfo);
400 m_Data->m_MergeInfoCache.insertKey(_m, path);
401 }
402 return _m.toBool();
403 }
404
singleInfo(const QString & what,const svn::Revision & _rev,svn::InfoEntry & target,const svn::Revision & _peg)405 bool SvnActions::singleInfo(const QString &what, const svn::Revision &_rev, svn::InfoEntry &target, const svn::Revision &_peg)
406 {
407 QString url;
408 QString cacheKey;
409 QTime d; d.start();
410 svn::Revision peg = _peg;
411 if (!m_Data->m_CurrentContext) {
412 return false;
413 }
414 #ifdef DEBUG_TIMER
415 QTime _counttime;
416 _counttime.start();
417 #endif
418
419 if (!svn::Url::isValid(what)) {
420 // working copy
421 // url = svn::Wc::getUrl(what);
422 url = what;
423 if (_rev != svn::Revision::WORKING && url.contains(QLatin1Char('@'))) {
424 url += QStringLiteral("@BASE");
425 }
426 peg = svn::Revision::UNDEFINED;
427 cacheKey = url;
428 } else {
429 // valid url
430 QUrl _uri(what);
431 QString prot = svn::Url::transformProtokoll(_uri.scheme());
432 _uri.setScheme(prot);
433 url = _uri.toString();
434 if (peg == svn::Revision::UNDEFINED) {
435 peg = _rev;
436 }
437 if (peg == svn::Revision::UNDEFINED) {
438 peg = svn::Revision::HEAD;
439 }
440 cacheKey = _rev.toString() + QLatin1Char('/') + url;
441 }
442 svn::InfoEntries e;
443 bool must_write = false;
444
445 {
446 QReadLocker rl(&(m_Data->m_InfoCacheLock));
447 if (cacheKey.isEmpty() || !m_Data->m_InfoCache.findSingleValid(cacheKey, target)) {
448 must_write = true;
449 try {
450 e = (m_Data->m_Svnclient->info(url, svn::DepthEmpty, _rev, peg));
451 } catch (const svn::Exception &ce) {
452 qCDebug(KDESVN_LOG) << "single info: " << ce.msg() << endl;
453 emit clientException(ce.msg());
454 return false;
455 }
456 if (e.isEmpty() || e[0].reposRoot().isEmpty()) {
457 emit clientException(i18n("Got no info."));
458 return false;
459 }
460 target = e[0];
461 }
462 }
463 if (must_write) {
464 QWriteLocker wl(&(m_Data->m_InfoCacheLock));
465 if (!cacheKey.isEmpty()) {
466 m_Data->m_InfoCache.insertKey(e[0], cacheKey);
467 if (peg != svn::Revision::UNDEFINED && peg.kind() != svn::Revision::NUMBER && peg.kind() != svn::Revision::DATE) {
468 // for persistent storage, store head into persistent cache makes no sense.
469 cacheKey = e[0].revision().toString() + QLatin1Char('/') + url;
470 m_Data->m_InfoCache.insertKey(e[0], cacheKey);
471 }
472 }
473 }
474 #ifdef DEBUG_TIMER
475 qCDebug(KDESVN_LOG) << "Time getting info for " << cacheKey << ": " << _counttime.elapsed();
476 #endif
477
478 return true;
479 }
480
makeTree(const QString & what,const svn::Revision & _rev,const svn::Revision & startr,const svn::Revision & endr)481 void SvnActions::makeTree(const QString &what, const svn::Revision &_rev, const svn::Revision &startr, const svn::Revision &endr)
482 {
483 svn::InfoEntry info;
484 if (!singleInfo(what, _rev, info)) {
485 return;
486 }
487 const QString reposRoot = info.reposRoot().toString();
488
489 if (Kdesvnsettings::fill_cache_on_tree()) {
490 stopFillCache();
491 }
492
493 QPointer<KSvnSimpleOkDialog> dlg(new KSvnSimpleOkDialog(QStringLiteral("revisiontree_dlg"), m_Data->m_ParentList->realWidget()));
494 dlg->setWindowTitle(i18nc("@title:window", "History of %1", info.url().toString().mid(reposRoot.length())));
495
496 RevisionTree *rt(new RevisionTree(m_Data->m_Svnclient, m_Data->m_SvnContextListener, reposRoot,
497 startr, endr,
498 info.url().toString().mid(reposRoot.length()), _rev, dlg));
499 if (rt->isValid()) {
500 RevTreeWidget *disp = rt->getView();
501 if (disp) {
502 dlg->addWidget(disp);
503 connect(disp, &RevTreeWidget::makeNorecDiff,
504 this, &SvnActions::makeNorecDiff);
505 connect(disp, &RevTreeWidget::makeRecDiff,
506 this, QOverload<const QString&, const svn::Revision&, const QString&, const svn::Revision&, QWidget*>::of(&SvnActions::makeDiff));
507 connect(disp, &RevTreeWidget::makeCat,
508 this, &SvnActions::slotMakeCat);
509 dlg->exec();
510 }
511 }
512 delete dlg;
513 }
514
makeBlame(const svn::Revision & start,const svn::Revision & end,SvnItem * k)515 void SvnActions::makeBlame(const svn::Revision &start, const svn::Revision &end, SvnItem *k)
516 {
517 if (k) {
518 makeBlame(start, end, k->fullName(), m_Data->m_ParentList->realWidget());
519 }
520 }
521
makeBlame(const svn::Revision & start,const svn::Revision & end,const QString & k,QWidget * _p,const svn::Revision & _peg,SimpleLogCb * _acb)522 void SvnActions::makeBlame(const svn::Revision &start, const svn::Revision &end, const QString &k, QWidget *_p, const svn::Revision &_peg, SimpleLogCb *_acb)
523 {
524 if (!m_Data->m_CurrentContext) {
525 return;
526 }
527 svn::AnnotatedFile blame;
528 QWidget *_parent = _p ? _p : m_Data->m_ParentList->realWidget();
529 bool mergeinfo = hasMergeInfo(m_Data->m_ParentList->baseUri().isEmpty() ? k : m_Data->m_ParentList->baseUri());
530
531 svn::AnnotateParameter params;
532 params.path(k).pegRevision(_peg == svn::Revision::UNDEFINED ? end : _peg).revisionRange(svn::RevisionRange(start, end)).includeMerged(mergeinfo);
533
534 try {
535 CursorStack a(Qt::BusyCursor);
536 StopDlg sdlg(m_Data->m_SvnContextListener, _parent,
537 i18nc("@title:window", "Annotate"), i18n("Annotate lines - hit Cancel for abort"));
538 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage);
539 m_Data->m_Svnclient->annotate(blame, params);
540 } catch (const svn::Exception &e) {
541 emit clientException(e.msg());
542 return;
543 }
544 if (blame.isEmpty()) {
545 QString ex = i18n("Got no annotate");
546 emit clientException(ex);
547 return;
548 }
549 EMIT_FINISHED;
550 BlameDisplay::displayBlame(_acb ? _acb : this, k, blame, _p);
551 }
552
makeGet(const svn::Revision & start,const QString & what,const QString & target,const svn::Revision & peg,QWidget * _dlgparent)553 bool SvnActions::makeGet(const svn::Revision &start, const QString &what, const QString &target,
554 const svn::Revision &peg, QWidget *_dlgparent)
555 {
556 if (!m_Data->m_CurrentContext) {
557 return false;
558 }
559 CursorStack a(Qt::BusyCursor);
560 QWidget *dlgp = _dlgparent ? _dlgparent : m_Data->m_ParentList->realWidget();
561 svn::Path p(what);
562 try {
563 StopDlg sdlg(m_Data->m_SvnContextListener, dlgp,
564 i18nc("@title:window", "Content Get"), i18n("Getting content - hit Cancel for abort"));
565 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage);
566 m_Data->m_Svnclient->get(p, target, start, peg);
567 } catch (const svn::Exception &e) {
568 emit clientException(e.msg());
569 return false;
570 } catch (...) {
571 QString ex = i18n("Error getting content");
572 emit clientException(ex);
573 return false;
574 }
575 return true;
576 }
577
slotMakeCat(const svn::Revision & start,const QString & what,const QString & disp,const svn::Revision & peg,QWidget * _dlgparent)578 void SvnActions::slotMakeCat(const svn::Revision &start, const QString &what, const QString &disp, const svn::Revision &peg, QWidget *_dlgparent)
579 {
580 QTemporaryFile content;
581 content.setAutoRemove(true);
582 // required otherwise it will not generate a unique name...
583 if (!content.open()) {
584 emit clientException(i18n("Error while open temporary file"));
585 return;
586 }
587 QString tname = content.fileName();
588 content.close();
589 QWidget *parent = _dlgparent ? _dlgparent : m_Data->m_ParentList->realWidget();
590
591 if (!makeGet(start, what, tname, peg, parent)) {
592 return;
593 }
594 EMIT_FINISHED;
595 QMimeDatabase db;
596 const QMimeType mimeType(db.mimeTypeForFile(tname));
597 KService::List offers = KMimeTypeTrader::self()->query(mimeType.name(),
598 QLatin1String("Application"),
599 QLatin1String("Type == 'Application' or (exist Exec)"));
600 if (offers.isEmpty() || offers.first()->exec().isEmpty()) {
601 offers = KMimeTypeTrader::self()->query(mimeType.name(),
602 QLatin1String("Application"),
603 QLatin1String("Type == 'Application'"));
604 }
605 KService::List::ConstIterator it = offers.constBegin();
606 for (; it != offers.constEnd(); ++it) {
607 if ((*it)->noDisplay()) {
608 continue;
609 }
610 break;
611 }
612 if (it != offers.constEnd()) {
613 content.setAutoRemove(false);
614 KRun::runService(**it, QList<QUrl>() << QUrl::fromLocalFile(tname), QApplication::activeWindow(), true);
615 return;
616 }
617
618 QFile file(tname);
619 file.open(QIODevice::ReadOnly);
620 const QByteArray co = file.readAll();
621
622 if (!co.isEmpty()) {
623 QPointer<KSvnSimpleOkDialog> dlg = new KSvnSimpleOkDialog(QStringLiteral("cat_display_dlg"), parent);
624 dlg->setWindowTitle(i18nc("@title:window", "Content of %1", disp));
625 QTextBrowser *ptr = new QTextBrowser(dlg);
626 ptr->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
627 ptr->setWordWrapMode(QTextOption::NoWrap);
628 ptr->setReadOnly(true);
629 ptr->setText(QString::fromUtf8(co, co.size()));
630 dlg->addWidget(ptr);
631 dlg->exec();
632 delete dlg;
633 } else {
634 KMessageBox::information(parent, i18n("Got no content."));
635 }
636 }
637
makeMkdir(const svn::Targets & targets,const QString & logMessage)638 bool SvnActions::makeMkdir(const svn::Targets &targets, const QString &logMessage)
639 {
640 if (!m_Data->m_CurrentContext || targets.targets().isEmpty()) {
641 return false;
642 }
643 try {
644 m_Data->m_Svnclient->mkdir(targets, logMessage);
645 } catch (const svn::Exception &e) {
646 emit clientException(e.msg());
647 return false;
648 }
649 return true;
650 }
651
makeMkdir(const QString & parentDir)652 QString SvnActions::makeMkdir(const QString &parentDir)
653 {
654 if (!m_Data->m_CurrentContext) {
655 return QString();
656 }
657 bool isOk = false;
658 const QString ex = QInputDialog::getText(m_Data->m_ParentList->realWidget(), i18n("New folder"), i18n("Enter folder name:"),
659 QLineEdit::Normal, QString(), &isOk);
660 if (!isOk || ex.isEmpty()) {
661 return QString();
662 }
663 svn::Path target(parentDir);
664 target.addComponent(ex);
665
666 try {
667 m_Data->m_Svnclient->mkdir(target, QString());
668 } catch (const svn::Exception &e) {
669 emit clientException(e.msg());
670 return QString();
671 }
672
673 return target.path();
674 }
675
getInfo(const SvnItemList & lst,const svn::Revision & rev,const svn::Revision & peg,bool recursive,bool all)676 QString SvnActions::getInfo(const SvnItemList &lst, const svn::Revision &rev, const svn::Revision &peg, bool recursive, bool all)
677 {
678 QString res;
679 for (auto it = lst.cbegin(); it != lst.cend(); ++it) {
680 if (all) {
681 res += QStringLiteral("<h4 align=\"center\">%1</h4>").arg((*it)->fullName());
682 }
683 res += getInfo((*it)->fullName(), rev, peg, recursive, all);
684 }
685 return res;
686 }
687
getInfo(const QString & _what,const svn::Revision & rev,const svn::Revision & peg,bool recursive,bool all)688 QString SvnActions::getInfo(const QString &_what, const svn::Revision &rev, const svn::Revision &peg, bool recursive, bool all)
689 {
690 if (!m_Data->m_CurrentContext) {
691 return QString();
692 }
693 svn::InfoEntries entries;
694 if (recursive) {
695 try {
696 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(),
697 i18nc("@title:window", "Details"), i18n("Retrieving information - hit Cancel for abort"));
698 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage);
699 QString path = _what;
700 if (_what.contains(QLatin1Char('@')) && !svn::Url::isValid(_what)) {
701 path += QLatin1String("@BASE");
702 }
703 entries = (m_Data->m_Svnclient->info(path,
704 recursive ? svn::DepthInfinity : svn::DepthEmpty,
705 rev,
706 peg));
707 } catch (const svn::Exception &e) {
708 emit clientException(e.msg());
709 return QString();
710 }
711 } else {
712 svn::InfoEntry info;
713 if (!singleInfo(_what, rev, info, peg)) {
714 return QString();
715 }
716 entries.append(info);
717 }
718 return getInfo(entries, _what, all);
719 }
720
getInfo(const svn::InfoEntries & entries,const QString & _what,bool all)721 QString SvnActions::getInfo(const svn::InfoEntries &entries, const QString &_what, bool all)
722 {
723 QString text;
724 static QString rb(QStringLiteral("<tr><td><nobr>"));
725 static QString re(QStringLiteral("</nobr></td></tr>\n"));
726 static QString cs(QStringLiteral("</nobr>:</td><td><nobr>"));
727 unsigned int val = 0;
728 for (auto it = entries.begin(); it != entries.end(); ++it) {
729 if (val > 0) {
730 text += QStringLiteral("<hline>");
731 }
732 ++val;
733 text += QStringLiteral("<p align=\"center\">");
734 text += QStringLiteral("<table cellspacing=0 cellpadding=0>");
735 if (!(*it).Name().isEmpty()) {
736 text += rb + i18n("Name") + cs + ((*it).Name()) + re;
737 }
738 if (all) {
739 text += rb + i18n("URL") + cs + ((*it).url().toDisplayString()) + re;
740 if (!(*it).reposRoot().toString().isEmpty()) {
741 text += rb + i18n("Canonical repository URL") + cs + ((*it).reposRoot().toDisplayString()) + re;
742 }
743 if (!(*it).checksum().isEmpty()) {
744 text += rb + i18n("Checksum") + cs + ((*it).checksum()) + re;
745 }
746 }
747 text += rb + i18n("Type") + cs;
748 switch ((*it).kind()) {
749 case svn_node_none:
750 text += i18n("Absent");
751 break;
752 case svn_node_file:
753 text += i18n("File");
754 break;
755 case svn_node_dir:
756 text += i18n("Folder");
757 break;
758 case svn_node_unknown:
759 default:
760 text += i18n("Unknown");
761 break;
762 }
763 text += re;
764 if ((*it).kind() == svn_node_file) {
765 text += rb + i18n("Size") + cs;
766 if ((*it).size() != svn::InfoEntry::SVNQT_SIZE_UNKNOWN) {
767 text += helpers::ByteToString((*it).size());
768 } else if ((*it).working_size() != svn::InfoEntry::SVNQT_SIZE_UNKNOWN) {
769 text += helpers::ByteToString((*it).working_size());
770 }
771 text += re;
772 }
773 if (all) {
774 text += rb + i18n("Schedule") + cs;
775 switch ((*it).Schedule()) {
776 case svn_wc_schedule_normal:
777 text += i18n("Normal");
778 break;
779 case svn_wc_schedule_add:
780 text += i18n("Addition");
781 break;
782 case svn_wc_schedule_delete:
783 text += i18n("Deletion");
784 break;
785 case svn_wc_schedule_replace:
786 text += i18n("Replace");
787 break;
788 default:
789 text += i18n("Unknown");
790 break;
791 }
792 text += re;
793 text += rb + i18n("UUID") + cs + ((*it).uuid()) + re;
794 }
795 text += rb + i18n("Last author") + cs + ((*it).cmtAuthor()) + re;
796 if ((*it).cmtDate().IsValid()) {
797 text += rb + i18n("Last committed") + cs + (*it).cmtDate().toString() + re;
798 }
799 text += rb + i18n("Last revision") + cs + (*it).cmtRev().toString() + re;
800 if ((*it).textTime().IsValid()) {
801 text += rb + i18n("Content last changed") + cs + (*it).textTime().toString() + re;
802 }
803 if (all) {
804 if ((*it).propTime().IsValid()) {
805 text += rb + i18n("Property last changed") + cs + (*it).propTime().toString() + re;
806 }
807 for (const auto & _cfi : (*it).conflicts()) {
808 text += rb + i18n("New version of conflicted file") + cs + (_cfi->theirFile());
809 }
810 if ((*it).prejfile().length()) {
811 text += rb + i18n("Property reject file") +
812 cs + ((*it).prejfile()) + re;
813 }
814
815 if (!(*it).copyfromUrl().isEmpty()) {
816 text += rb + i18n("Copy from URL") + cs + ((*it).copyfromUrl().toDisplayString()) + re;
817 }
818 if ((*it).lockEntry().Locked()) {
819 text += rb + i18n("Lock token") + cs + ((*it).lockEntry().Token()) + re;
820 text += rb + i18n("Owner") + cs + ((*it).lockEntry().Owner()) + re;
821 text += rb + i18n("Locked on") + cs +
822 (*it).lockEntry().Date().toString() +
823 re;
824 text += rb + i18n("Lock comment") + cs +
825 (*it).lockEntry().Comment() + re;
826 } else {
827 svn::StatusPtr d;
828 if (checkReposLockCache(_what, d) && d && d->lockEntry().Locked()) {
829 text += rb + i18n("Lock token") + cs + (d->lockEntry().Token()) + re;
830 text += rb + i18n("Owner") + cs + (d->lockEntry().Owner()) + re;
831 text += rb + i18n("Locked on") + cs +
832 d->lockEntry().Date().toString() +
833 re;
834 text += rb + i18n("Lock comment") + cs +
835 d->lockEntry().Comment() + re;
836 }
837 }
838 }
839 text += QStringLiteral("</table></p>\n");
840 }
841 return text;
842 }
843
makeInfo(const SvnItemList & lst,const svn::Revision & rev,const svn::Revision & peg,bool recursive)844 void SvnActions::makeInfo(const SvnItemList &lst, const svn::Revision &rev, const svn::Revision &peg, bool recursive)
845 {
846 QStringList infoList;
847 infoList.reserve(lst.size());
848 for (const auto item : lst) {
849 const QString text = getInfo(item->fullName(), rev, peg, recursive, true);
850 if (!text.isEmpty()) {
851 infoList += text;
852 }
853 }
854 showInfo(infoList);
855 }
856
makeInfo(const QStringList & lst,const svn::Revision & rev,const svn::Revision & peg,bool recursive)857 void SvnActions::makeInfo(const QStringList &lst, const svn::Revision &rev, const svn::Revision &peg, bool recursive)
858 {
859 QStringList infoList;
860 infoList.reserve(lst.size());
861 for (int i = 0; i < lst.size(); ++i) {
862 const QString text = getInfo(lst.at(i), rev, peg, recursive, true);
863 if (!text.isEmpty()) {
864 infoList += text;
865 }
866 }
867 showInfo(infoList);
868 }
869
showInfo(const QStringList & infoList)870 void SvnActions::showInfo(const QStringList &infoList)
871 {
872 if (infoList.isEmpty()) {
873 return;
874 }
875 QString text(QLatin1String("<html><head></head><body>"));
876 for (int i = 0; i < infoList.count(); ++i) {
877 text += QLatin1String("<h4 align=\"center\">") + infoList.at(i) + QLatin1String("</h4>");
878 }
879 text += QLatin1String("</body></html>");
880
881 QPointer<KSvnSimpleOkDialog> dlg = new KSvnSimpleOkDialog(QStringLiteral("info_dialog"), QApplication::activeModalWidget());
882 dlg->setWindowTitle(i18nc("@title:window", "Infolist"));
883 QTextBrowser *ptr = new QTextBrowser(dlg);
884 dlg->addWidget(ptr);
885 ptr->setReadOnly(true);
886 ptr->setText(text);
887 dlg->exec();
888 delete dlg;
889 }
890
891
editProperties(SvnItem * k,const svn::Revision & rev)892 void SvnActions::editProperties(SvnItem *k, const svn::Revision &rev)
893 {
894 if (!m_Data->m_CurrentContext) {
895 return;
896 }
897 if (!k) {
898 return;
899 }
900 QPointer<PropertiesDlg> dlg(new PropertiesDlg(k, svnclient(), rev));
901 connect(dlg, SIGNAL(clientException(QString)), m_Data->m_ParentList->realWidget(), SLOT(slotClientException(QString)));
902 if (dlg->exec() != QDialog::Accepted) {
903 delete dlg;
904 return;
905 }
906 svn::PropertiesMap setList;
907 QStringList delList;
908 dlg->changedItems(setList, delList);
909 changeProperties(setList, delList, k->fullName());
910 k->refreshStatus();
911 EMIT_FINISHED;
912 delete dlg;
913 }
914
changeProperties(const svn::PropertiesMap & setList,const QStringList & delList,const QString & path,const svn::Depth & depth)915 bool SvnActions::changeProperties(const svn::PropertiesMap &setList, const QStringList &delList, const QString &path, const svn::Depth &depth)
916 {
917 try {
918 svn::PropertiesParameter params;
919 params.path(path).depth(depth);
920 StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(),
921 i18nc("@title:window", "Applying Properties"), i18n("<center>Applying<br/>hit cancel for abort</center>"));
922 connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage);
923 // propertyValue == QString::null -> delete property
924 for (int pos = 0; pos < delList.size(); ++pos) {
925 m_Data->m_Svnclient->propset(params.propertyName(delList.at(pos)));
926 }
927 for (svn::PropertiesMap::ConstIterator it = setList.begin(); it != setList.end(); ++it) {
928 m_Data->m_Svnclient->propset(params.propertyName(it.key()).propertyValue(it.value()));
929 }
930 } catch (const svn::Exception &e) {
931 emit clientException(e.msg());
932 return false;
933 }
934 return true;
935 }
936
937 /*!
938 \fn SvnActions::slotCommit()
939 */
doCommit(const SvnItemList & which)940 void SvnActions::doCommit(const SvnItemList &which)
941 {
942 if (!m_Data->m_CurrentContext || !m_Data->m_ParentList->isWorkingCopy()) {
943 return;
944 }
945
946 svn::Paths targets;
947 if (which.isEmpty()) {
948 targets.push_back(svn::Path(QStringLiteral(".")));
949 } else {
950 targets.reserve(which.size());
951 for (const SvnItem *item : which) {
952 targets.push_back(svn::Path(
953 m_Data->m_ParentList->relativePath(item)
954 ));
955 }
956 }
957 if (!m_Data->m_ParentList->baseUri().isEmpty()) {
958 if (!QDir::setCurrent(m_Data->m_ParentList->baseUri())) {
959 QString msg = i18n("Could not change to folder %1\n", m_Data->m_ParentList->baseUri())
960 + QString::fromLocal8Bit(strerror(errno));
961 emit sendNotify(msg);
962 }
963 }
964 if (makeCommit(svn::Targets(targets)) && Kdesvnsettings::log_cache_on_open()) {
965 startFillCache(m_Data->m_ParentList->baseUri(), true);
966 }
967 }
968
makeCommit(const svn::Targets & targets)969 bool SvnActions::makeCommit(const svn::Targets &targets)
970 {
971 bool ok, keeplocks;
972 svn::Depth depth;
973 svn::Revision nnum;
974 bool review = Kdesvnsettings::review_commit();
975 QString msg;
976
977 if (!doNetworking()) {
978 emit clientException(i18n("Not commit because networking is disabled"));
979 return false;
980 }
981
982 svn::CommitParameter commit_parameters;
983 stopFillCache();
984 if (!review) {
985 msg = Commitmsg_impl::getLogmessage(&ok, &depth, &keeplocks, m_Data->m_ParentList->realWidget());
986 if (!ok) {
987 return false;
988 }
989 commit_parameters.targets(targets);
990 } else {
991 CommitActionEntries _check, _uncheck, _result;
992 svn::StatusEntries _Cache;
993 depth = svn::DepthEmpty;
994 svn::StatusParameter params;
995 params.depth(svn::DepthInfinity).all(false).update(false).noIgnore(false).revision(svn::Revision::HEAD);
996 /// @todo filter out double entries
997 for (const auto &tgt : targets.targets()) {
998 try {
999 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
slotProcessDataRead(const QByteArray & data,WatchedProcess *)1067 void SvnActions::slotProcessDataRead(const QByteArray &data, WatchedProcess *)
1068 {
1069 emit sendNotify(QString::fromLocal8Bit(data));
1070 }
1071
get(const QString & what,const QString & to,const svn::Revision & rev,const svn::Revision & peg,QWidget * p)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 */
makeDiff(const QString & what,const svn::Revision & start,const svn::Revision & end,const svn::Revision & _peg,bool isDir)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
makeDiff(const QString & p1,const svn::Revision & start,const QString & p2,const svn::Revision & end)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
makeDiff(const QString & p1,const svn::Revision & start,const QString & p2,const svn::Revision & end,QWidget * p)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
makeDiffExternal(const QString & p1,const svn::Revision & start,const QString & p2,const svn::Revision & end,const svn::Revision & _peg,bool isDir,QWidget * p,bool rec)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
makeDiff(const QString & p1,const svn::Revision & start,const QString & p2,const svn::Revision & end,const svn::Revision & _peg,bool isDir,QWidget * p)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
makeDiffinternal(const QString & p1,const svn::Revision & r1,const QString & p2,const svn::Revision & r2,QWidget * p,const svn::Revision & _peg)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
makeNorecDiff(const QString & p1,const svn::Revision & r1,const QString & p2,const svn::Revision & r2,QWidget * _p)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
dispDiff(const QByteArray & ex)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 */
makeUpdate(const svn::Targets & targets,const svn::Revision & rev,svn::Depth depth)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 */
slotUpdateHeadRec()1458 void SvnActions::slotUpdateHeadRec()
1459 {
1460 prepareUpdate(false);
1461 }
1462
1463
1464 /*!
1465 \fn SvnActions::prepareUpdate(bool ask)
1466 */
prepareUpdate(bool ask)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 */
slotUpdateTo()1497 void SvnActions::slotUpdateTo()
1498 {
1499 prepareUpdate(true);
1500 }
1501
1502
1503 /*!
1504 \fn SvnActions::slotAdd()
1505 */
slotAdd()1506 void SvnActions::slotAdd()
1507 {
1508 makeAdd(false);
1509 }
1510
slotAddRec()1511 void SvnActions::slotAddRec()
1512 {
1513 makeAdd(true);
1514 }
1515
makeAdd(bool rec)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
addItems(const svn::Paths & items,svn::Depth depth)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
makeDelete(const QStringList & w)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 */
makeDelete(const svn::Targets & target,bool keep_local,bool force)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
slotCheckout()1587 void SvnActions::slotCheckout()
1588 {
1589 CheckoutExport(QUrl(), false);
1590 }
1591
slotExport()1592 void SvnActions::slotExport()
1593 {
1594 CheckoutExport(QUrl(), true);
1595 }
1596
slotCheckoutCurrent()1597 void SvnActions::slotCheckoutCurrent()
1598 {
1599 CheckoutExportCurrent(false);
1600 }
1601
slotExportCurrent()1602 void SvnActions::slotExportCurrent()
1603 {
1604 CheckoutExportCurrent(true);
1605 }
1606
CheckoutExport(const QUrl & what,bool _exp,bool urlisTarget)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
CheckoutExportCurrent(bool _exp)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
makeCheckout(const QString & rUrl,const QString & tPath,const svn::Revision & r,const svn::Revision & _peg,svn::Depth depth,bool _exp,bool openIt,bool ignoreExternal,bool overwrite,bool ignoreKeywords,QWidget * _p)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
slotRevert()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
slotRevertItems(const QStringList & displist)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
makeSwitch(const QUrl & rUrl,const QString & tPath,const svn::Revision & r,svn::Depth depth,const svn::Revision & peg,bool stickydepth,bool ignore_externals,bool allow_unversioned)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
makeRelocate(const QUrl & fUrl,const QUrl & tUrl,const QString & path,bool recursive,bool ignore_externals)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
slotSwitch()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
makeSwitch(const QString & path,const QUrl & what)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
makeCleanup(const QString & path)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
slotResolved(const QString & path)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
slotResolve(const QString & p)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
slotImport(const QString & path,const QUrl & target,const QString & message,svn::Depth depth,bool noIgnore,bool noUnknown)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
slotMergeExternal(const QString & _src1,const QString & _src2,const QString & _target,const svn::Revision & rev1,const svn::Revision & rev2,const svn::Revision & _peg,bool rec)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
slotMergeWcRevisions(const QString & _entry,const svn::Revision & rev1,const svn::Revision & rev2,bool rec,bool ancestry,bool forceIt,bool dry,bool allow_mixed_rev)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
slotMerge(const QString & src1,const QString & src2,const QString & target,const svn::Revision & rev1,const svn::Revision & rev2,const svn::Revision & _peg,bool rec,bool ancestry,bool forceIt,bool dry,bool recordOnly,bool reintegrate,bool allow_mixed_rev)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 */
makeMove(const QString & Old,const QString & New)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
makeMove(const QList<QUrl> & Old,const QString & New)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
makeCopy(const QString & Old,const QString & New,const svn::Revision & rev)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
makeCopy(const QList<QUrl> & Old,const QString & New,const svn::Revision & rev)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 */
makeLock(const QStringList & what,const QString & _msg,bool breakit)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 */
makeUnlock(const QStringList & what,bool breakit)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 */
makeStatus(const QString & what,svn::StatusEntries & dlist,const svn::Revision & where,bool rec,bool all)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
makeStatus(const QString & what,svn::StatusEntries & dlist,const svn::Revision & where,bool rec,bool all,bool display_ignores,bool updates)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
makeStatus(const QString & what,svn::StatusEntries & dlist,const svn::Revision & where,svn::Depth _d,bool all,bool display_ignores,bool updates)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
checkAddItems(const QString & path,bool print_error_box)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
stopCheckModifiedThread()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
stopCheckUpdateThread()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
stopFillCache()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
stopMain()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
killallThreads()2453 void SvnActions::killallThreads()
2454 {
2455 stopMain();
2456 stopCheckModifiedThread();
2457 stopCheckUpdateThread();
2458 stopFillCache();
2459 }
2460
createModifiedCache(const QString & what)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
checkModifiedThread()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
checkUpdateThread()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
getaddedItems(const QString & path,svn::StatusEntries & target)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
checkUpdatesRunning()2542 bool SvnActions::checkUpdatesRunning()
2543 {
2544 return m_UThread && m_UThread->isRunning();
2545 }
2546
addModifiedCache(const svn::StatusPtr & what)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
deleteFromModifiedCache(const QString & what)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
checkModifiedCache(const QString & path) const2565 bool SvnActions::checkModifiedCache(const QString &path) const
2566 {
2567 return m_Data->m_Cache.find(path);
2568 }
2569
checkReposLockCache(const QString & path) const2570 bool SvnActions::checkReposLockCache(const QString &path) const
2571 {
2572 return m_Data->m_repoLockCache.findSingleValid(path, false);
2573 }
2574
checkReposLockCache(const QString & path,svn::StatusPtr & t) const2575 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
checkConflictedCache(const QString & path) const2581 bool SvnActions::checkConflictedCache(const QString &path) const
2582 {
2583 return m_Data->m_conflictCache.find(path);
2584 }
2585
startFillCache(const QString & path,bool startup)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
doNetworking()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 */
createUpdateCache(const QString & what)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
checkUpdateCache(const QString & path) const2651 bool SvnActions::checkUpdateCache(const QString &path)const
2652 {
2653 return m_Data->m_UpdateCache.find(path);
2654 }
2655
removeFromUpdateCache(const QStringList & what,bool exact_only)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
isUpdated(const QString & path) const2663 bool SvnActions::isUpdated(const QString &path)const
2664 {
2665 svn::StatusPtr d;
2666 return getUpdated(path, d);
2667 }
2668
getUpdated(const QString & path,svn::StatusPtr & d) const2669 bool SvnActions::getUpdated(const QString &path, svn::StatusPtr &d)const
2670 {
2671 return m_Data->m_UpdateCache.findSingleValid(path, d);
2672 }
2673
clearUpdateCache()2674 void SvnActions::clearUpdateCache()
2675 {
2676 m_Data->m_UpdateCache.clear();
2677 }
2678
makeIgnoreEntry(const svn::Path & item,const QStringList & ignorePattern,bool unignore)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
makeIgnoreEntry(SvnItem * which,bool unignore)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
propList(const QString & which,const svn::Revision & where,bool cacheOnly)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
isLockNeeded(SvnItem * which,const svn::Revision & where)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
searchProperty(QString & Store,const QString & property,const QString & start,const svn::Revision & where,bool up)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
makeList(const QString & url,svn::DirEntries & dlist,const svn::Revision & where,svn::Depth depth)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
isLocalWorkingCopy(const QString & path,QUrl & repoUrl)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
slotExtraLogMsg(const QString & msg)2870 void SvnActions::slotExtraLogMsg(const QString &msg)
2871 {
2872 emit sigExtraLogMsg(msg);
2873 }
2874
slotCancel(bool how)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
setContextData(const QString & aKey,const QString & aValue)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
clearContextData()2895 void SvnActions::clearContextData()
2896 {
2897 m_Data->m_contextData.clear();
2898 }
2899
getContextData(const QString & aKey) const2900 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
threadRunning(ThreadType which) const2908 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 }
2920