File indexing completed on 2024-05-12 05:44:26

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2009 by Rajko Albrecht                             *
0003  *   ral@alwins-world.de                                                   *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
0019  ***************************************************************************/
0020 #include "revisiontree.h"
0021 #include "../ccontextlistener.h"
0022 #include "../stopdlg.h"
0023 #include "elogentry.h"
0024 #include "revgraphview.h"
0025 #include "revtreewidget.h"
0026 #include "settings/kdesvnsettings.h"
0027 #include "svnfrontend/fronthelpers/cursorstack.h"
0028 #include "svnqt/cache/LogCache.h"
0029 #include "svnqt/cache/ReposConfig.h"
0030 #include "svnqt/cache/ReposLog.h"
0031 #include "svnqt/client_parameter.h"
0032 #include "svnqt/log_entry.h"
0033 #include "svnqt/url.h"
0034 
0035 #include <KLocalizedString>
0036 #include <KMessageBox>
0037 
0038 #include <QProgressDialog>
0039 #include <QWidget>
0040 
0041 #define INTERNALCOPY 1
0042 #define INTERNALRENAME 2
0043 
0044 class CContextListener;
0045 class RtreeData
0046 {
0047 public:
0048     RtreeData();
0049     virtual ~RtreeData();
0050 
0051     QMap<long, eLog_Entry> m_History;
0052 
0053     svn::LogEntriesMap m_OldHistory;
0054 
0055     long max_rev, min_rev;
0056     QProgressDialog *progress;
0057     QTime m_stopTick;
0058 
0059     QWidget *dlgParent;
0060     RevTreeWidget *m_TreeDisplay;
0061 
0062     svn::ClientP m_Client;
0063     CContextListener *m_Listener;
0064 
0065     bool getLogs(const QString &, const svn::Revision &startr, const svn::Revision &endr, const QString &origin);
0066 };
0067 
0068 RtreeData::RtreeData()
0069     : max_rev(-1)
0070     , min_rev(-1)
0071 {
0072     progress = nullptr;
0073     m_TreeDisplay = nullptr;
0074     dlgParent = nullptr;
0075     m_Listener = nullptr;
0076 }
0077 
0078 RtreeData::~RtreeData()
0079 {
0080     delete progress;
0081 }
0082 
0083 bool RtreeData::getLogs(const QString &reposRoot, const svn::Revision &startr, const svn::Revision &endr, const QString &origin)
0084 {
0085     Q_UNUSED(origin);
0086     if (!m_Listener || !m_Client) {
0087         return false;
0088     }
0089     svn::LogParameter params;
0090     params.targets(reposRoot).revisionRange(endr, startr).peg(startr).limit(0).discoverChangedPathes(true).strictNodeHistory(false);
0091     const svn::StringArray ex(svn::cache::ReposConfig::self()->readEntry(reposRoot, "tree_exclude_list", QStringList()));
0092     try {
0093         CursorStack a(Qt::BusyCursor);
0094         StopDlg sdlg(m_Listener, dlgParent, i18nc("@title:window", "Logs"), i18n("Getting logs - hit Cancel for abort"));
0095         if (svn::Url::isLocal(reposRoot)) {
0096             m_Client->log(params.excludeList(ex), m_OldHistory);
0097         } else {
0098             svn::cache::ReposLog rl(m_Client, reposRoot);
0099             if (rl.isValid()) {
0100                 rl.simpleLog(m_OldHistory, startr, endr, (!Kdesvnsettings::network_on() || !Kdesvnsettings::fill_cache_on_tree()), ex);
0101             } else if (Kdesvnsettings::network_on()) {
0102                 m_Client->log(params.excludeList(ex), m_OldHistory);
0103             } else {
0104                 KMessageBox::error(nullptr,
0105                                    i18n("Could not retrieve logs, reason:\n%1", i18n("No log cache possible due broken database and networking not allowed.")));
0106                 return false;
0107             }
0108         }
0109     } catch (const svn::Exception &ce) {
0110         KMessageBox::error(nullptr, i18n("Could not retrieve logs, reason:\n%1", ce.msg()));
0111         return false;
0112     }
0113     return true;
0114 }
0115 
0116 RevisionTree::RevisionTree(const svn::ClientP &aClient,
0117                            CContextListener *aListener,
0118                            const QString &reposRoot,
0119                            const svn::Revision &startr,
0120                            const svn::Revision &endr,
0121                            const QString &origin,
0122                            const svn::Revision &baserevision,
0123                            QWidget *parent)
0124     : m_InitialRevsion(0)
0125     , m_Path(origin)
0126     , m_Valid(false)
0127 {
0128     m_Data = new RtreeData;
0129     m_Data->m_Client = aClient;
0130     m_Data->m_Listener = aListener;
0131     m_Data->dlgParent = parent;
0132 
0133     if (!m_Data->getLogs(reposRoot, startr, endr, origin)) {
0134         return;
0135     }
0136 
0137     long possible_rev = -1;
0138 
0139     m_Data->progress = new QProgressDialog(i18n("Scanning the logs for %1", origin), i18n("Cancel"), 0, m_Data->m_OldHistory.size(), parent);
0140     m_Data->progress->setWindowTitle(i18nc("@title:window", "Scanning logs"));
0141     m_Data->progress->setMinimumDuration(100);
0142     m_Data->progress->setAutoClose(false);
0143     m_Data->progress->setWindowModality(Qt::WindowModal);
0144     bool cancel = false;
0145     int count = 0;
0146     for (auto it = m_Data->m_OldHistory.begin(); it != m_Data->m_OldHistory.end(); ++it) {
0147         m_Data->progress->setValue(count);
0148         QCoreApplication::processEvents();
0149         if (m_Data->progress->wasCanceled()) {
0150             cancel = true;
0151             break;
0152         }
0153         if (it.key() > m_Data->max_rev) {
0154             m_Data->max_rev = it.key();
0155         }
0156         if (it.key() < m_Data->min_rev || m_Data->min_rev == -1) {
0157             m_Data->min_rev = it.key();
0158         }
0159         if (baserevision.kind() == svn_opt_revision_date) {
0160             if ((baserevision.date() <= it.value().date && possible_rev == -1) || possible_rev > it.key()) {
0161                 possible_rev = it.key();
0162             }
0163         }
0164         ++count;
0165     }
0166     if (baserevision.kind() == svn_opt_revision_head || baserevision.kind() == svn_opt_revision_working) {
0167         m_Baserevision = m_Data->max_rev;
0168     } else if (baserevision.kind() == svn_opt_revision_number) {
0169         m_Baserevision = baserevision.revnum();
0170     } else if (baserevision.kind() == svn_opt_revision_date) {
0171         m_Baserevision = possible_rev;
0172     } else {
0173         m_Baserevision = m_Data->min_rev;
0174     }
0175     if (!cancel) {
0176         if (topDownScan()) {
0177             m_Data->progress->setAutoReset(true);
0178             m_Data->progress->setRange(0, 100);
0179             m_Data->m_stopTick.restart();
0180             m_Data->m_TreeDisplay = new RevTreeWidget(m_Data->m_Client);
0181             if (bottomUpScan(m_InitialRevsion, 0, m_Path, 0)) {
0182                 m_Valid = true;
0183                 m_Data->m_TreeDisplay->setBasePath(reposRoot);
0184                 m_Data->m_TreeDisplay->dumpRevtree();
0185             } else {
0186                 delete m_Data->m_TreeDisplay;
0187                 m_Data->m_TreeDisplay = nullptr;
0188             }
0189         }
0190     }
0191     m_Data->progress->hide();
0192 }
0193 
0194 RevisionTree::~RevisionTree()
0195 {
0196     delete m_Data;
0197 }
0198 
0199 bool RevisionTree::isDeleted(long revision, const QString &path)
0200 {
0201     for (long i = 0; i < m_Data->m_History[revision].changedPaths.count(); ++i) {
0202         if (isParent(m_Data->m_History[revision].changedPaths[i].path, path) && m_Data->m_History[revision].changedPaths[i].action == 'D') {
0203             return true;
0204         }
0205     }
0206     return false;
0207 }
0208 
0209 bool RevisionTree::topDownScan()
0210 {
0211     m_Data->progress->setRange(0, m_Data->max_rev - m_Data->min_rev);
0212     bool cancel = false;
0213     QString label;
0214     QString olabel = m_Data->progress->labelText();
0215     for (long j = m_Data->max_rev; j >= m_Data->min_rev; --j) {
0216         m_Data->progress->setValue(m_Data->max_rev - j);
0217         QCoreApplication::processEvents();
0218         if (m_Data->progress->wasCanceled()) {
0219             cancel = true;
0220             break;
0221         }
0222         for (long i = 0; i < m_Data->m_OldHistory[j].changedPaths.count(); ++i) {
0223             if (i > 0 && i % 100 == 0) {
0224                 if (m_Data->progress->wasCanceled()) {
0225                     cancel = true;
0226                     break;
0227                 }
0228                 label = i18n("%1<br>Check change entry %2 of %3", olabel, i, m_Data->m_OldHistory[j].changedPaths.count());
0229                 m_Data->progress->setLabelText(label);
0230                 QCoreApplication::processEvents();
0231             }
0232             /* find min revision of item */
0233             if (m_Data->m_OldHistory[j].changedPaths[i].action == 'A' && isParent(m_Data->m_OldHistory[j].changedPaths[i].path, m_Path)) {
0234                 if (!m_Data->m_OldHistory[j].changedPaths[i].copyFromPath.isEmpty()) {
0235                     if (m_InitialRevsion < m_Data->m_OldHistory[j].revision) {
0236                         QString r = m_Path.mid(m_Data->m_OldHistory[j].changedPaths[i].path.length());
0237                         m_Path = m_Data->m_OldHistory[j].changedPaths[i].copyFromPath;
0238                         m_Path += r;
0239                     }
0240                 } else if (m_Data->m_OldHistory[j].changedPaths[i].path == m_Path && m_Data->m_OldHistory[j].changedPaths[i].copyToPath.isEmpty()) {
0241                     // here it is added
0242                     m_InitialRevsion = m_Data->m_OldHistory[j].revision;
0243                 }
0244             }
0245         }
0246     }
0247     if (cancel == true) {
0248         return false;
0249     }
0250     m_Data->progress->setLabelText(olabel);
0251     /* find forward references and filter them out */
0252     for (long j = m_Data->max_rev; j >= m_Data->min_rev; --j) {
0253         m_Data->progress->setValue(m_Data->max_rev - j);
0254         QCoreApplication::processEvents();
0255         if (m_Data->progress->wasCanceled()) {
0256             cancel = true;
0257             break;
0258         }
0259         for (long i = 0; i < m_Data->m_OldHistory[j].changedPaths.count(); ++i) {
0260             if (i > 0 && i % 100 == 0) {
0261                 if (m_Data->progress->wasCanceled()) {
0262                     cancel = true;
0263                     break;
0264                 }
0265                 label = i18n("%1<br>Check change entry %2 of %3", olabel, i, m_Data->m_OldHistory[j].changedPaths.count());
0266                 m_Data->progress->setLabelText(label);
0267                 QCoreApplication::processEvents();
0268             }
0269             if (!m_Data->m_OldHistory[j].changedPaths[i].copyFromPath.isEmpty()) {
0270                 long r = m_Data->m_OldHistory[j].changedPaths[i].copyFromRevision;
0271                 QString sourcepath = m_Data->m_OldHistory[j].changedPaths[i].copyFromPath;
0272                 char a = m_Data->m_OldHistory[j].changedPaths[i].action;
0273                 if (m_Data->m_OldHistory[j].changedPaths[i].path.isEmpty()) {
0274                     continue;
0275                 }
0276                 if (a == 'R') {
0277                     m_Data->m_OldHistory[j].changedPaths[i].action = 0;
0278                 } else if (a == 'A') {
0279                     a = INTERNALCOPY;
0280                     for (long z = 0; z < m_Data->m_OldHistory[j].changedPaths.count(); ++z) {
0281                         if (m_Data->m_OldHistory[j].changedPaths[z].action == 'D' && isParent(m_Data->m_OldHistory[j].changedPaths[z].path, sourcepath)) {
0282                             a = INTERNALRENAME;
0283                             m_Data->m_OldHistory[j].changedPaths[z].action = 0;
0284                             break;
0285                         }
0286                     }
0287                     m_Data->m_History[r].addCopyTo(sourcepath, m_Data->m_OldHistory[j].changedPaths[i].path, j, a, r);
0288                     m_Data->m_OldHistory[j].changedPaths[i].action = 0;
0289                 }
0290             }
0291         }
0292     }
0293     if (cancel == true) {
0294         return false;
0295     }
0296     m_Data->progress->setLabelText(olabel);
0297     for (long j = m_Data->max_rev; j >= m_Data->min_rev; --j) {
0298         m_Data->progress->setValue(m_Data->max_rev - j);
0299         QCoreApplication::processEvents();
0300         if (m_Data->progress->wasCanceled()) {
0301             cancel = true;
0302             break;
0303         }
0304         for (long i = 0; i < m_Data->m_OldHistory[j].changedPaths.count(); ++i) {
0305             if (m_Data->m_OldHistory[j].changedPaths[i].action == 0) {
0306                 continue;
0307             }
0308             if (i > 0 && i % 100 == 0) {
0309                 if (m_Data->progress->wasCanceled()) {
0310                     cancel = true;
0311                     break;
0312                 }
0313                 label = i18n("%1<br>Check change entry %2 of %3", olabel, i, m_Data->m_OldHistory[j].changedPaths.count());
0314                 m_Data->progress->setLabelText(label);
0315                 QCoreApplication::processEvents();
0316             }
0317             m_Data->m_History[j].addCopyTo(m_Data->m_OldHistory[j].changedPaths[i].path, QString(), -1, m_Data->m_OldHistory[j].changedPaths[i].action);
0318         }
0319         m_Data->m_History[j].author = m_Data->m_OldHistory[j].author;
0320         m_Data->m_History[j].date = m_Data->m_OldHistory[j].date;
0321         m_Data->m_History[j].revision = m_Data->m_OldHistory[j].revision;
0322         m_Data->m_History[j].message = m_Data->m_OldHistory[j].message;
0323     }
0324     return !cancel;
0325 }
0326 
0327 bool RevisionTree::isParent(const QString &_par, const QString &tar)
0328 {
0329     if (_par == tar) {
0330         return true;
0331     }
0332     QString par = _par.endsWith(QLatin1Char('/')) ? _par : _par + QLatin1Char('/');
0333     return tar.startsWith(par);
0334 }
0335 
0336 bool RevisionTree::isValid() const
0337 {
0338     return m_Valid;
0339 }
0340 
0341 static QString uniqueNodeName(long rev, const QString &path)
0342 {
0343     QString res = QString::fromUtf8(path.toLocal8Bit().toBase64());
0344     res.replace(QLatin1Char('\"'), QLatin1String("_quot_"));
0345     res.replace(QLatin1Char(' '), QLatin1String("_space_"));
0346     QString n = QString::asprintf("%05ld", rev);
0347     return QLatin1Char('\"') + n + QLatin1Char('_') + res + QLatin1Char('\"');
0348 }
0349 
0350 bool RevisionTree::bottomUpScan(long startrev, unsigned recurse, const QString &_path, long _last)
0351 {
0352 #define REVENTRY m_Data->m_History[j]
0353 #define FORWARDENTRY m_Data->m_History[j].changedPaths[i]
0354 
0355     QString path = _path;
0356     long lastrev = _last;
0357 #ifdef DEBUG_PARSE
0358     qCDebug(KDESVN_LOG) << "Searching for " << path << " at revision " << startrev << " recursion " << recurse << Qt::endl;
0359 #endif
0360     bool cancel = false;
0361     for (long j = startrev; j <= m_Data->max_rev; ++j) {
0362         if (m_Data->m_stopTick.elapsed() > 500) {
0363             m_Data->progress->setValue(m_Data->progress->value() + 1);
0364             QCoreApplication::processEvents();
0365             m_Data->m_stopTick.restart();
0366         }
0367         if (m_Data->progress->wasCanceled()) {
0368             cancel = true;
0369             break;
0370         }
0371         for (long i = 0; i < REVENTRY.changedPaths.count(); ++i) {
0372             if (!isParent(FORWARDENTRY.path, path)) {
0373                 continue;
0374             }
0375             QString n1, n2;
0376             if (isParent(FORWARDENTRY.path, path)) {
0377                 bool get_out = false;
0378                 if (FORWARDENTRY.path != path) {
0379 #ifdef DEBUG_PARSE
0380                     qCDebug(KDESVN_LOG) << "Parent rename? " << FORWARDENTRY.path << " -> " << FORWARDENTRY.copyToPath << " -> " << FORWARDENTRY.copyFromPath
0381                                         << Qt::endl;
0382 #endif
0383                 }
0384                 if (FORWARDENTRY.action == INTERNALCOPY || FORWARDENTRY.action == INTERNALRENAME) {
0385                     bool ren = FORWARDENTRY.action == INTERNALRENAME;
0386                     QString tmpPath = path;
0387                     QString recPath;
0388                     if (FORWARDENTRY.copyToPath.length() == 0) {
0389                         continue;
0390                     }
0391                     QString r = path.mid(FORWARDENTRY.path.length());
0392                     recPath = FORWARDENTRY.copyToPath;
0393                     recPath += r;
0394                     n1 = uniqueNodeName(lastrev, tmpPath);
0395                     n2 = uniqueNodeName(FORWARDENTRY.copyToRevision, recPath);
0396                     if (lastrev > 0) {
0397                         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n1].targets.append(RevGraphView::targetData(n2, FORWARDENTRY.action));
0398                     }
0399                     m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].name = recPath;
0400                     m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].rev = FORWARDENTRY.copyToRevision;
0401                     m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Action = FORWARDENTRY.action;
0402                     m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Author = m_Data->m_History[FORWARDENTRY.copyToRevision].author;
0403                     m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Message = m_Data->m_History[FORWARDENTRY.copyToRevision].message;
0404                     m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Date = svn::DateTime(m_Data->m_History[FORWARDENTRY.copyToRevision].date).toString();
0405                     if (ren) {
0406                         lastrev = FORWARDENTRY.copyToRevision;
0407                         /* skip items between */
0408 #ifdef DEBUG_PARSE
0409                         qCDebug(KDESVN_LOG) << "Renamed to " << recPath << " at revision " << FORWARDENTRY.copyToRevision << Qt::endl;
0410 #endif
0411                         j = lastrev;
0412                         path = recPath;
0413                     } else {
0414 #ifdef DEBUG_PARSE
0415                         qCDebug(KDESVN_LOG) << "Copy to " << recPath << Qt::endl;
0416 #endif
0417                         if (!bottomUpScan(FORWARDENTRY.copyToRevision, recurse + 1, recPath, FORWARDENTRY.copyToRevision)) {
0418                             return false;
0419                         }
0420                     }
0421                 } else if (FORWARDENTRY.path == path) {
0422                     switch (FORWARDENTRY.action) {
0423                     case 'A':
0424 #ifdef DEBUG_PARSE
0425                         qCDebug(KDESVN_LOG) << "Inserting adding base item" << Qt::endl;
0426 #endif
0427                         n1 = uniqueNodeName(j, FORWARDENTRY.path);
0428                         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n1].Action = FORWARDENTRY.action;
0429                         fillItem(j, i, n1, path);
0430                         lastrev = j;
0431                         break;
0432                     case 'M':
0433                     case 'R':
0434 #ifdef DEBUG_PARSE
0435                         qCDebug(KDESVN_LOG) << "Item modified at revision " << j << " recurse " << recurse << Qt::endl;
0436 #endif
0437                         n1 = uniqueNodeName(j, FORWARDENTRY.path);
0438                         n2 = uniqueNodeName(lastrev, FORWARDENTRY.path);
0439                         if (lastrev > 0) {
0440                             m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1, FORWARDENTRY.action));
0441                         }
0442                         fillItem(j, i, n1, path);
0443                         /* modify of same item (in same recurse) should be only once at a revision
0444                          * so check if lastrev==j must not be done but will cost cpu ticks so I always
0445                          * set trev and lastrev.
0446                          */
0447                         lastrev = j;
0448                         break;
0449                     case 'D':
0450 #ifdef DEBUG_PARSE
0451                         qCDebug(KDESVN_LOG) << "(Sloppy match) Item deleted at revision " << j << " recurse " << recurse << Qt::endl;
0452 #endif
0453                         n1 = uniqueNodeName(j, path);
0454                         n2 = uniqueNodeName(lastrev, path);
0455                         if (n1 == n2) {
0456                             /* cvs import - copy and deletion at same revision.
0457                              * CVS sucks.
0458                              */
0459                             n1 = uniqueNodeName(j, "D_" + path);
0460                         }
0461                         if (lastrev > 0) {
0462                             m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1, FORWARDENTRY.action));
0463                         }
0464                         fillItem(j, i, n1, path);
0465                         lastrev = j;
0466                         get_out = true;
0467                         break;
0468                     default:
0469                         break;
0470                     }
0471                 } else {
0472                     switch (FORWARDENTRY.action) {
0473                     case 'D':
0474 #ifdef DEBUG_PARSE
0475                         qCDebug(KDESVN_LOG) << "(Exact match) Item deleted at revision " << j << " recurse " << recurse << Qt::endl;
0476 #endif
0477                         n1 = uniqueNodeName(j, path);
0478                         n2 = uniqueNodeName(lastrev, path);
0479                         if (n1 == n2) {
0480                             /* cvs import - copy and deletion at same revision.
0481                              * CVS sucks.
0482                              */
0483                             n1 = uniqueNodeName(j, "D_" + path);
0484                         }
0485                         if (lastrev > 0) {
0486                             m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1, FORWARDENTRY.action));
0487                         }
0488                         fillItem(j, i, n1, path);
0489                         lastrev = j;
0490                         get_out = true;
0491                         break;
0492                     default:
0493                         break;
0494                     }
0495                 }
0496                 if (get_out) {
0497                     return true;
0498                 }
0499             }
0500         }
0501     }
0502     return !cancel;
0503 }
0504 
0505 RevTreeWidget *RevisionTree::getView()
0506 {
0507     return m_Data->m_TreeDisplay;
0508 }
0509 
0510 void RevisionTree::fillItem(long rev, int pathIndex, const QString &nodeName, const QString &path)
0511 {
0512     m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].name = path;
0513     m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].rev = rev;
0514     if (pathIndex >= 0) {
0515         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Action = m_Data->m_History[rev].changedPaths[pathIndex].action;
0516         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Author = m_Data->m_History[rev].author;
0517         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Message = m_Data->m_History[rev].message;
0518         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Date = svn::DateTime(m_Data->m_History[rev].date).toString();
0519     } else {
0520         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Action = 0;
0521         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Author.clear();
0522         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Message.clear();
0523         m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Date = svn::DateTime(0).toString();
0524     }
0525 }