File indexing completed on 2024-05-05 04:41:01

0001 /*
0002     SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "svndiffjob.h"
0008 #include "svndiffjob_p.h"
0009 
0010 #include <QMutexLocker>
0011 #include <QRegExp>
0012 #include <QStringList>
0013 #include <QFileInfo>
0014 
0015 #include <KLocalizedString>
0016 
0017 #include <vcs/vcsrevision.h>
0018 
0019 #include "kdevsvncpp/path.hpp"
0020 #include "kdevsvncpp/revision.hpp"
0021 
0022 #include "icore.h"
0023 #include "iruncontroller.h"
0024 
0025 #include "svnclient.h"
0026 
0027 ///@todo The subversion library returns borked diffs, where the headers are at the end. This function
0028 ///           takes those headers, and moves them into the correct place to create a valid working diff.
0029 ///           Find the source of this problem.
0030 QString repairDiff(const QString& diff) {
0031     qCDebug(PLUGIN_SVN) << "diff before repair:" << diff;
0032     QStringList lines = diff.split(QLatin1Char('\n'));
0033     QMap<QString, QString> headers;
0034     for(int a = 0; a < lines.size()-1; ++a) {
0035         const QLatin1String indexLineBegin("Index: ");
0036         if(lines[a].startsWith(indexLineBegin) && lines[a+1].startsWith(QLatin1String("====="))) {
0037             const QString fileName = lines[a].midRef(indexLineBegin.size()).trimmed().toString();
0038             headers[fileName] = lines[a];
0039             qCDebug(PLUGIN_SVN) << "found header for" << fileName;
0040             lines[a] = QString();
0041             if(lines[a+1].startsWith(QLatin1String("======"))) {
0042                 headers[fileName] += QLatin1Char('\n') + lines[a+1];
0043             lines[a+1] = QString();
0044             }
0045         }
0046     }
0047 
0048     QRegExp spaceRegExp(QStringLiteral("\\s"));
0049 
0050     for(int a = 0; a < lines.size()-1; ++a) {
0051         const QLatin1String threeDashLineBegin("--- ");
0052         if(lines[a].startsWith(threeDashLineBegin)) {
0053             QString tail = lines[a].mid(threeDashLineBegin.size());
0054             if(tail.indexOf(spaceRegExp) != -1) {
0055                 QString file = tail.left(tail.indexOf(spaceRegExp));
0056                 qCDebug(PLUGIN_SVN) << "checking for" << file;
0057                 const auto headerIt = headers.constFind(file);
0058                 if (headerIt != headers.constEnd()) {
0059                     qCDebug(PLUGIN_SVN) << "adding header for" << file << ":" << *headerIt;
0060                     lines[a] = *headerIt + QLatin1Char('\n') + lines[a];
0061                 }
0062             }
0063         }
0064     }
0065     QString ret = lines.join(QLatin1Char('\n'));
0066     qCDebug(PLUGIN_SVN) << "repaired diff:" << ret;
0067     return ret;
0068 }
0069 
0070 //@TODO: Handle raw diffs by using SvnCatJob to fetch both files/revisions
0071 
0072 SvnInternalDiffJob::SvnInternalDiffJob( SvnJobBase* parent )
0073     : SvnInternalJobBase( parent )
0074 {
0075     m_pegRevision.setRevisionValue( KDevelop::VcsRevision::Head,
0076                                     KDevelop::VcsRevision::Special );
0077 }
0078 
0079 void SvnInternalDiffJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread* /*thread*/)
0080 {
0081     initBeforeRun();
0082 
0083     SvnClient cli(m_ctxt);
0084     try
0085     {
0086 
0087         QString diff;
0088         if( destination().isValid() )
0089         {
0090             QByteArray srcba;
0091             if( source().type() == KDevelop::VcsLocation::LocalLocation )
0092             {
0093                 srcba = source().localUrl().toString( QUrl::PreferLocalFile | QUrl::StripTrailingSlash ).toUtf8();
0094             }else
0095             {
0096                 srcba = source().repositoryServer().toUtf8();
0097             }
0098             QByteArray dstba;
0099             if( destination().type() == KDevelop::VcsLocation::LocalLocation )
0100             {
0101                 dstba = destination().localUrl().toString( QUrl::PreferLocalFile | QUrl::StripTrailingSlash ).toUtf8();
0102             }else
0103             {
0104                 dstba = destination().repositoryServer().toUtf8();
0105             }
0106             svn::Revision srcRev = createSvnCppRevisionFromVcsRevision( srcRevision() );
0107             svn::Revision dstRev = createSvnCppRevisionFromVcsRevision( dstRevision() );
0108             if( srcba.isEmpty() || ( dstba.isEmpty() && srcRev.kind() == svn_opt_revision_unspecified
0109                 && dstRev.kind() == svn_opt_revision_unspecified ) )
0110             {
0111                 throw svn::ClientException( "Not enough information for a diff");
0112             }
0113             diff = cli.diff( svn::Path( srcba.data() ), srcRev, svn::Path( dstba.data() ),
0114                              dstRev, recursive(), ignoreAncestry(),
0115                              noDiffOnDelete(), ignoreContentType() );
0116         }else
0117         {
0118             QByteArray srcba;
0119             if( source().type() == KDevelop::VcsLocation::LocalLocation )
0120             {
0121                 srcba = source().localUrl().toString( QUrl::PreferLocalFile | QUrl::StripTrailingSlash ).toUtf8();
0122             }else
0123             {
0124                 srcba = source().repositoryServer().toUtf8();
0125             }
0126             svn::Revision pegRev = createSvnCppRevisionFromVcsRevision( pegRevision() );
0127             svn::Revision srcRev = createSvnCppRevisionFromVcsRevision( srcRevision() );
0128             svn::Revision dstRev = createSvnCppRevisionFromVcsRevision( dstRevision() );
0129             if( srcba.isEmpty() || pegRev.kind() == svn_opt_revision_unspecified
0130                 || dstRev.kind() == svn_opt_revision_unspecified
0131                 || srcRev.kind() == svn_opt_revision_unspecified)
0132             {
0133                 throw svn::ClientException( "Not enough information for a diff");
0134             }
0135             diff = cli.diff( svn::Path( srcba.data() ), pegRev, srcRev,
0136                              dstRev, recursive(), ignoreAncestry(),
0137                              noDiffOnDelete(), ignoreContentType() );
0138         }
0139         diff = repairDiff(diff);
0140         emit gotDiff( diff );
0141 
0142     }catch( const svn::ClientException& ce )
0143     {
0144         qCDebug(PLUGIN_SVN) << "Exception while doing a diff: "
0145                 << m_source.localUrl() << m_source.repositoryServer() << m_srcRevision.prettyValue()
0146                 << m_destination.localUrl() << m_destination.repositoryServer() << m_dstRevision.prettyValue()
0147                 << QString::fromUtf8( ce.message() );
0148         setErrorMessage( QString::fromUtf8( ce.message() ) );
0149         m_success = false;
0150     }
0151 }
0152 
0153 
0154 void SvnInternalDiffJob::setSource( const KDevelop::VcsLocation& src )
0155 {
0156     QMutexLocker l( &m_mutex );
0157     m_source = src;
0158 }
0159 void SvnInternalDiffJob::setDestination( const KDevelop::VcsLocation& dst )
0160 {
0161     QMutexLocker l( &m_mutex );
0162     m_destination = dst;
0163 }
0164 void SvnInternalDiffJob::setSrcRevision( const KDevelop::VcsRevision& srcRev )
0165 {
0166     QMutexLocker l( &m_mutex );
0167     m_srcRevision = srcRev;
0168 }
0169 void SvnInternalDiffJob::setDstRevision( const KDevelop::VcsRevision& dstRev )
0170 {
0171     QMutexLocker l( &m_mutex );
0172     m_dstRevision = dstRev;
0173 }
0174 void SvnInternalDiffJob::setPegRevision( const KDevelop::VcsRevision& pegRev )
0175 {
0176     QMutexLocker l( &m_mutex );
0177     m_pegRevision = pegRev;
0178 }
0179 void SvnInternalDiffJob::setRecursive( bool recursive )
0180 {
0181     QMutexLocker l( &m_mutex );
0182     m_recursive = recursive;
0183 }
0184 void SvnInternalDiffJob::setIgnoreAncestry( bool ignoreAncestry )
0185 {
0186     QMutexLocker l( &m_mutex );
0187     m_ignoreAncestry = ignoreAncestry;
0188 }
0189 void SvnInternalDiffJob::setIgnoreContentType( bool ignoreContentType )
0190 {
0191     QMutexLocker l( &m_mutex );
0192     m_ignoreContentType = ignoreContentType;
0193 }
0194 void SvnInternalDiffJob::setNoDiffOnDelete( bool noDiffOnDelete )
0195 {
0196     QMutexLocker l( &m_mutex );
0197     m_noDiffOnDelete = noDiffOnDelete;
0198 }
0199 
0200 bool SvnInternalDiffJob::recursive() const
0201 {
0202     QMutexLocker l( &m_mutex );
0203     return m_recursive;
0204 }
0205 bool SvnInternalDiffJob::ignoreAncestry() const
0206 {
0207     QMutexLocker l( &m_mutex );
0208     return m_ignoreAncestry;
0209 }
0210 bool SvnInternalDiffJob::ignoreContentType() const
0211 {
0212     QMutexLocker l( &m_mutex );
0213     return m_ignoreContentType;
0214 }
0215 bool SvnInternalDiffJob::noDiffOnDelete() const
0216 {
0217     QMutexLocker l( &m_mutex );
0218     return m_noDiffOnDelete;
0219 }
0220 KDevelop::VcsLocation SvnInternalDiffJob::source() const
0221 {
0222     QMutexLocker l( &m_mutex );
0223     return m_source;
0224 }
0225 KDevelop::VcsLocation SvnInternalDiffJob::destination() const
0226 {
0227     QMutexLocker l( &m_mutex );
0228     return m_destination;
0229 }
0230 KDevelop::VcsRevision SvnInternalDiffJob::srcRevision() const
0231 {
0232     QMutexLocker l( &m_mutex );
0233     return m_srcRevision;
0234 }
0235 KDevelop::VcsRevision SvnInternalDiffJob::dstRevision() const
0236 {
0237     QMutexLocker l( &m_mutex );
0238     return m_dstRevision;
0239 }
0240 KDevelop::VcsRevision SvnInternalDiffJob::pegRevision() const
0241 {
0242     QMutexLocker l( &m_mutex );
0243     return m_pegRevision;
0244 }
0245 
0246 SvnDiffJob::SvnDiffJob( KDevSvnPlugin* parent )
0247     : SvnJobBaseImpl( parent, KDevelop::OutputJob::Silent )
0248 {
0249     setType( KDevelop::VcsJob::Add );
0250     connect( m_job.data(), &SvnInternalDiffJob::gotDiff,
0251                 this, &SvnDiffJob::setDiff, Qt::QueuedConnection );
0252 
0253     setObjectName(i18n("Subversion Diff"));
0254 }
0255 
0256 QVariant SvnDiffJob::fetchResults()
0257 {
0258     return QVariant::fromValue(m_diff);
0259 }
0260 
0261 void SvnDiffJob::start()
0262 {
0263     if( !m_job->source().isValid()
0264          || ( !m_job->destination().isValid() &&
0265                 ( m_job->srcRevision().revisionType() == KDevelop::VcsRevision::Invalid
0266                  || m_job->dstRevision().revisionType() == KDevelop::VcsRevision::Invalid ) )
0267       )
0268     {
0269         internalJobFailed();
0270         setErrorText( i18n( "Not enough information given to execute diff" ) );
0271     } else {
0272         startInternalJob();
0273     }
0274 }
0275 
0276 void SvnDiffJob::setSource( const KDevelop::VcsLocation& source )
0277 {
0278     if( status() == KDevelop::VcsJob::JobNotStarted )
0279         m_job->setSource( source );
0280 }
0281 void SvnDiffJob::setDestination( const KDevelop::VcsLocation& destination )
0282 {
0283     if( status() == KDevelop::VcsJob::JobNotStarted )
0284         m_job->setDestination( destination );
0285 }
0286 void SvnDiffJob::setPegRevision( const KDevelop::VcsRevision& pegRevision )
0287 {
0288     if( status() == KDevelop::VcsJob::JobNotStarted )
0289         m_job->setPegRevision( pegRevision );
0290 }
0291 
0292 void SvnDiffJob::setSrcRevision( const KDevelop::VcsRevision& srcRevision )
0293 {
0294     if( status() == KDevelop::VcsJob::JobNotStarted )
0295         m_job->setSrcRevision( srcRevision );
0296 }
0297 void SvnDiffJob::setDstRevision( const KDevelop::VcsRevision& dstRevision )
0298 {
0299     if( status() == KDevelop::VcsJob::JobNotStarted )
0300         m_job->setDstRevision( dstRevision );
0301 }
0302 void SvnDiffJob::setRecursive( bool recursive )
0303 {
0304     if( status() == KDevelop::VcsJob::JobNotStarted )
0305         m_job->setRecursive( recursive );
0306 }
0307 void SvnDiffJob::setIgnoreAncestry( bool ignoreAncestry )
0308 {
0309     if( status() == KDevelop::VcsJob::JobNotStarted )
0310         m_job->setIgnoreAncestry( ignoreAncestry );
0311 }
0312 void SvnDiffJob::setIgnoreContentType( bool ignoreContentType )
0313 {
0314     if( status() == KDevelop::VcsJob::JobNotStarted )
0315         m_job->setIgnoreContentType( ignoreContentType );
0316 }
0317 void SvnDiffJob::setNoDiffOnDelete( bool noDiffOnDelete )
0318 {
0319     if( status() == KDevelop::VcsJob::JobNotStarted )
0320         m_job->setNoDiffOnDelete( noDiffOnDelete );
0321 }
0322 
0323 void SvnDiffJob::setDiff( const QString& diff )
0324 {
0325     m_diff = KDevelop::VcsDiff();
0326     m_diff.setBaseDiff(QUrl::fromLocalFile(QStringLiteral("/")));
0327     m_diff.setDiff( diff );
0328 
0329     emit resultsReady( this );
0330 }
0331 
0332 #include "moc_svndiffjob.cpp"
0333 #include "moc_svndiffjob_p.cpp"