/*************************************************************************** * Copyright (C) 2005-2007 by Rajko Albrecht * * ral@alwins-world.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "revisiontree.h" #include "../stopdlg.h" #include "src/svnqt/log_entry.hpp" #include "src/svnqt/cache/LogCache.hpp" #include "src/svnqt/cache/ReposLog.hpp" #include "src/svnqt/url.hpp" #include "helpers/sub2qt.h" #include "revtreewidget.h" #include "revgraphview.h" #include "elogentry.h" #include "src/svnfrontend/fronthelpers/cursorstack.h" #include "src/settings/tdesvnsettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define INTERNALCOPY 1 #define INTERNALRENAME 2 class RtreeData { public: RtreeData(); virtual ~RtreeData(); TQMap m_History; svn::LogEntriesMap m_OldHistory; long max_rev,min_rev; KProgressDialog*progress; TQTime m_stopTick; TQWidget*dlgParent; RevTreeWidget*m_TreeDisplay; svn::Client*m_Client; TQObject* m_Listener; bool getLogs(const TQString&,const svn::Revision&startr,const svn::Revision&endr,const TQString&origin); }; RtreeData::RtreeData() : max_rev(-1),min_rev(-1) { progress=0; m_TreeDisplay = 0; m_Client = 0; dlgParent = 0; m_Listener = 0; } RtreeData::~RtreeData() { delete progress; } bool RtreeData::getLogs(const TQString&reposRoot,const svn::Revision&startr,const svn::Revision&endr,const TQString&origin) { if (!m_Listener||!m_Client) { return false; } try { CursorStack a(TQt::BusyCursor); StopDlg sdlg(m_Listener,dlgParent, 0,"Logs",i18n("Getting logs - hit cancel for abort")); if (svn::Url::isLocal(reposRoot) ) { m_Client->log(reposRoot,endr,startr,m_OldHistory,startr,true,false,0); } else { svn::cache::ReposLog rl(m_Client,reposRoot); if (rl.isValid()) { rl.simpleLog(m_OldHistory,startr,endr,!Kdesvnsettings::network_on()); } else if (Kdesvnsettings::network_on()) { m_Client->log(reposRoot,endr,startr,m_OldHistory,startr,true,false,0); } else { KMessageBox::error(0,i18n("Could not retrieve logs, reason:\n%1").tqarg(i18n("No logcache possible due broken database and networking not allowed."))); return false; } } } catch (const svn::Exception&ce) { kdDebug()<m_Client=aClient; m_Data->m_Listener=aListener; m_Data->dlgParent=parent; if (!m_Data->getLogs(reposRoot,startr,endr,origin)) { return; } long possible_rev=-1; kdDebug()<<"Origin: "<progress=new KProgressDialog( parent,"progressdlg",i18n("Scanning logs"),i18n("Scanning the logs for %1").tqarg(origin),true); m_Data->progress->setMinimumDuration(100); m_Data->progress->show(); m_Data->progress->setAllowCancel(true); m_Data->progress->progressBar()->setTotalSteps(m_Data->m_OldHistory.size()); m_Data->progress->setAutoClose(false); m_Data->progress->show(); bool cancel=false; svn::LogEntriesMap::Iterator it; unsigned count = 0; for (it=m_Data->m_OldHistory.begin();it!=m_Data->m_OldHistory.end();++it) { m_Data->progress->progressBar()->setProgress(count); kapp->processEvents(); if (m_Data->progress->wasCancelled()) { cancel=true; break; } if (it.key()>m_Data->max_rev) { m_Data->max_rev=it.key(); } if (it.key()min_rev||m_Data->min_rev==-1) { m_Data->min_rev=it.key(); } if (baserevision.kind()==svn_opt_revision_date) { if (baserevision.date()<=it.data().date && possible_rev==-1||possible_rev>it.key()) { possible_rev=it.key(); } } ++count; } if (baserevision.kind()==svn_opt_revision_head||baserevision.kind()==svn_opt_revision_working) { m_Baserevision=m_Data->max_rev; } else if (baserevision.kind()==svn_opt_revision_number) { m_Baserevision=baserevision.revnum(); } else if (baserevision.kind()==svn_opt_revision_date) { m_Baserevision=possible_rev; } if (!cancel) { kdDebug( )<<" max revision " << m_Data->max_rev << " min revision " << m_Data->min_rev << endl; if (topDownScan()) { kdDebug()<<"topdown end"<progress->setAutoReset(true); m_Data->progress->progressBar()->setTotalSteps(100); m_Data->progress->progressBar()->setPercentageVisible(false); m_Data->m_stopTick.restart(); m_Data->m_TreeDisplay=new RevTreeWidget(m_Data->m_Listener,m_Data->m_Client,treeParent); if (bottomUpScan(m_InitialRevsion,0,m_Path,0)) { kdDebug()<<"Bottom up end"<m_TreeDisplay->setBasePath(reposRoot); m_Data->m_TreeDisplay->dumpRevtree(); } else { delete m_Data->m_TreeDisplay; m_Data->m_TreeDisplay = 0; } } } else { kdDebug()<<"Canceld"<progress->hide(); } RevisionTree::~RevisionTree() { delete m_Data; } bool RevisionTree::isDeleted(long revision,const TQString&path) { for (unsigned i = 0;im_History[revision].changedPaths.count();++i) { if (isParent(m_Data->m_History[revision].changedPaths[i].path,path) && m_Data->m_History[revision].changedPaths[i].action=='D') { return true; } } return false; } bool RevisionTree::topDownScan() { m_Data->progress->progressBar()->setTotalSteps(m_Data->max_rev-m_Data->min_rev); bool cancel=false; TQString label; TQString olabel = m_Data->progress->labelText(); for (long j=m_Data->max_rev;j>=m_Data->min_rev;--j) { m_Data->progress->progressBar()->setProgress(m_Data->max_rev-j); kapp->processEvents(); if (m_Data->progress->wasCancelled()) { cancel=true; break; } for (unsigned i = 0; im_OldHistory[j].changedPaths.count();++i) { if (i>0 && i%100==0) { if (m_Data->progress->wasCancelled()) { cancel=true; break; } label = i18n("%1
Check change entry %2 of %3") .tqarg(olabel).tqarg(i).tqarg(m_Data->m_OldHistory[j].changedPaths.count()); m_Data->progress->setLabel(label); kapp->processEvents(); } /* find min revision of item */ if (m_Data->m_OldHistory[j].changedPaths[i].action=='A'&& isParent(m_Data->m_OldHistory[j].changedPaths[i].path,m_Path)) { if (!m_Data->m_OldHistory[j].changedPaths[i].copyFromPath.isEmpty()) { if (m_InitialRevsionm_OldHistory[j].revision) { TQString tmpPath = m_Path; TQString r = m_Path.mid(m_Data->m_OldHistory[j].changedPaths[i].path.length()); m_Path=m_Data->m_OldHistory[j].changedPaths[i].copyFromPath; m_Path+=r; } } else if (m_Data->m_OldHistory[j].changedPaths[i].path==m_Path && m_Data->m_OldHistory[j].changedPaths[i].copyToPath.isEmpty()){ // here it is added m_InitialRevsion = m_Data->m_OldHistory[j].revision; } } } } kdDebug()<<"Stage one done"<progress->setLabel(olabel); /* find forward references and filter them out */ for (long j=m_Data->max_rev;j>=m_Data->min_rev;--j) { m_Data->progress->progressBar()->setProgress(m_Data->max_rev-j); kapp->processEvents(); if (m_Data->progress->wasCancelled()) { cancel=true; break; } for (unsigned i = 0; im_OldHistory[j].changedPaths.count();++i) { if (i>0 && i%100==0) { if (m_Data->progress->wasCancelled()) { cancel=true; break; } label = i18n("%1
Check change entry %2 of %3").tqarg(olabel).tqarg(i).tqarg(m_Data->m_OldHistory[j].changedPaths.count()); m_Data->progress->setLabel(label); kapp->processEvents(); } if (!m_Data->m_OldHistory[j].changedPaths[i].copyFromPath.isEmpty()) { long r = m_Data->m_OldHistory[j].changedPaths[i].copyFromRevision; TQString sourcepath = m_Data->m_OldHistory[j].changedPaths[i].copyFromPath; char a = m_Data->m_OldHistory[j].changedPaths[i].action; if (m_Data->m_OldHistory[j].changedPaths[i].path.isEmpty()) { kdDebug()<<"Empty entry! rev " << j << " source " << sourcepath << endl; continue; } if (a=='R') { m_Data->m_OldHistory[j].changedPaths[i].action=0; } else if (a=='A'){ a=INTERNALCOPY; for (unsigned z = 0;zm_OldHistory[j].changedPaths.count();++z) { if (m_Data->m_OldHistory[j].changedPaths[z].action=='D' && isParent(m_Data->m_OldHistory[j].changedPaths[z].path,sourcepath) ) { a=INTERNALRENAME; m_Data->m_OldHistory[j].changedPaths[z].action=0; break; } } m_Data->m_History[r].addCopyTo(sourcepath,m_Data->m_OldHistory[j].changedPaths[i].path,j,a,r); m_Data->m_OldHistory[j].changedPaths[i].action=0; } else { kdDebug()<<"Action with source path but wrong action \""<progress->setLabel(olabel); for (long j=m_Data->max_rev;j>=m_Data->min_rev;--j) { m_Data->progress->progressBar()->setProgress(m_Data->max_rev-j); kapp->processEvents(); if (m_Data->progress->wasCancelled()) { cancel=true; break; } for (unsigned i = 0; im_OldHistory[j].changedPaths.count();++i) { if (m_Data->m_OldHistory[j].changedPaths[i].action==0) { continue; } if (i>0 && i%100==0) { if (m_Data->progress->wasCancelled()) { cancel=true; break; } label = i18n("%1
Check change entry %2 of %3").tqarg(olabel).tqarg(i).tqarg(m_Data->m_OldHistory[j].changedPaths.count()); m_Data->progress->setLabel(label); kapp->processEvents(); } m_Data->m_History[j].addCopyTo(m_Data->m_OldHistory[j].changedPaths[i].path,TQString(),-1,m_Data->m_OldHistory[j].changedPaths[i].action); } m_Data->m_History[j].author=m_Data->m_OldHistory[j].author; m_Data->m_History[j].date=m_Data->m_OldHistory[j].date; m_Data->m_History[j].revision=m_Data->m_OldHistory[j].revision; m_Data->m_History[j].message=m_Data->m_OldHistory[j].message; } kdDebug()<<"Stage three done"<m_History[j] #define FORWARDENTRY m_Data->m_History[j].changedPaths[i] TQString path = _path; long lastrev = _last; /* this is required if an item will modified AND copied at same revision.*/ long trev = -1; #ifdef DEBUG_PARSE kdDebug()<<"Searching for "<max_rev;++j) { if (m_Data->m_stopTick.elapsed()>500) { m_Data->progress->progressBar()->advance(1); kapp->processEvents(); m_Data->m_stopTick.restart(); } if (m_Data->progress->wasCancelled()) { cancel=true; break; } for (unsigned i=0;i " << FORWARDENTRY.copyToPath << " -> " << FORWARDENTRY.copyFromPath << endl; #endif } if (FORWARDENTRY.action==INTERNALCOPY || FORWARDENTRY.action==INTERNALRENAME ) { bool ren = FORWARDENTRY.action==INTERNALRENAME; TQString tmpPath = path; TQString recPath; if (FORWARDENTRY.copyToPath.length()==0) { continue; } TQString r = path.mid(FORWARDENTRY.path.length()); recPath= FORWARDENTRY.copyToPath; recPath+=r; n1 = uniqueNodeName(lastrev,tmpPath); n2 = uniqueNodeName(FORWARDENTRY.copyToRevision,recPath); if (lastrev>0) { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n1].targets.append(RevGraphView::targetData(n2,FORWARDENTRY.action)); } m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].name=recPath; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].rev = FORWARDENTRY.copyToRevision; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Action=FORWARDENTRY.action; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Author=m_Data->m_History[FORWARDENTRY.copyToRevision].author; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Message=m_Data->m_History[FORWARDENTRY.copyToRevision].message; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Date=helpers::sub2qt::apr_time2qtString(m_Data->m_History[FORWARDENTRY.copyToRevision].date); if (ren) { lastrev = FORWARDENTRY.copyToRevision; /* skip items between */ j=lastrev; #ifdef DEBUG_PARSE kdDebug()<<"Renamed to "<< recPath << " at revision " << FORWARDENTRY.copyToRevision << endl; #endif path=recPath; } else { #ifdef DEBUG_PARSE kdDebug()<<"Copy to "<< recPath << endl; #endif if (!bottomUpScan(FORWARDENTRY.copyToRevision,recurse+1,recPath,FORWARDENTRY.copyToRevision)) { return false; } } } else if (FORWARDENTRY.path==path) { switch (FORWARDENTRY.action) { case 'A': #ifdef DEBUG_PARSE kdDebug()<<"Inserting adding base item"<m_TreeDisplay->m_RevGraphView->m_Tree[n1].Action=FORWARDENTRY.action; fillItem(j,i,n1,path); lastrev=j; break; case 'M': case 'R': #ifdef DEBUG_PARSE kdDebug()<<"Item modified at revision "<< j << " recurse " << recurse << endl; #endif n1 = uniqueNodeName(j,FORWARDENTRY.path); n2 = uniqueNodeName(lastrev,FORWARDENTRY.path); if (lastrev>0) m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1,FORWARDENTRY.action)); fillItem(j,i,n1,path); /* modify of same item (in same recurse) should be only once at a revision * so check if lastrev==j must not be done but will cost cpu ticks so I always * set trev and lastrev. */ trev = lastrev; lastrev = j; break; case 'D': #ifdef DEBUG_PARSE kdDebug()<<"(Sloppy match) Item deleted at revision "<< j << " recurse " << recurse << endl; #endif n1 = uniqueNodeName(j,path); n2 = uniqueNodeName(lastrev,path); if (n1==n2) { /* cvs import - copy and deletion at same revision. * CVS sucks. */ n1 = uniqueNodeName(j,"D_"+path); } if (lastrev>0) m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1,FORWARDENTRY.action)); fillItem(j,i,n1,path); lastrev = j; get_out= true; break; default: break; } } else { switch (FORWARDENTRY.action) { case 'D': #ifdef DEBUG_PARSE kdDebug()<<"(Exact match) Item deleted at revision "<< j << " recurse " << recurse << endl; #endif n1 = uniqueNodeName(j,path); n2 = uniqueNodeName(lastrev,path); if (n1==n2) { /* cvs import - copy and deletion at same revision. * CVS sucks. */ n1 = uniqueNodeName(j,"D_"+path); } if (lastrev>0) m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1,FORWARDENTRY.action)); fillItem(j,i,n1,path); lastrev = j; get_out = true; break; default: break; } } if (get_out) { return true; } } } } return !cancel; } TQWidget*RevisionTree::getView() { return m_Data->m_TreeDisplay; } void RevisionTree::fillItem(long rev,int pathIndex,const TQString&nodeName,const TQString&path) { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].name=path; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].rev = rev; if (pathIndex>=0) { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Action=m_Data->m_History[rev].changedPaths[pathIndex].action; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Author=m_Data->m_History[rev].author; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Message=m_Data->m_History[rev].message; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Date=helpers::sub2qt::apr_time2qtString(m_Data->m_History[rev].date); } else { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Action=0; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Author=""; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Message=""; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Date=helpers::sub2qt::apr_time2qtString(0); } }