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"