svndiffjob.cpp 12 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/***************************************************************************
 *   This file is part of KDevelop                                         *
 *   Copyright 2007 Andreas Pakulat <apaku@gmx.de>                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library 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 Library 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 "svndiffjob.h"
#include "svndiffjob_p.h"

#include <QMutexLocker>
#include <QRegExp>
#include <QStringList>
#include <QFileInfo>

29
#include <KLocalizedString>
30

31
#include <vcs/vcsrevision.h>
32

33 34
#include "kdevsvncpp/path.hpp"
#include "kdevsvncpp/revision.hpp"
35

36 37 38
#include "icore.h"
#include "iruncontroller.h"

39 40
#include "svnclient.h"

41 42 43
///@todo The subversion library returns borked diffs, where the headers are at the end. This function
///           takes those headers, and moves them into the correct place to create a valid working diff.
///           Find the source of this problem.
44
QString repairDiff(const QString& diff) {
45
    qCDebug(PLUGIN_SVN) << "diff before repair:" << diff;
46
    QStringList lines = diff.split(QLatin1Char('\n'));
47 48
    QMap<QString, QString> headers;
    for(int a = 0; a < lines.size()-1; ++a) {
49
        if(lines[a].startsWith(QLatin1String("Index: ")) && lines[a+1].startsWith(QLatin1String("====="))) {
50
            const QString fileName = lines[a].midRef(strlen("Index: ")).trimmed().toString();
51
            headers[fileName] = lines[a];
52
            qCDebug(PLUGIN_SVN) << "found header for" << fileName;
53
            lines[a] = QString();
54
            if(lines[a+1].startsWith(QLatin1String("======"))) {
55
                headers[fileName] += QLatin1Char('\n') + lines[a+1];
56 57 58 59
            lines[a+1] = QString();
            }
        }
    }
60

61
    QRegExp spaceRegExp(QStringLiteral("\\s"));
62

63
    for(int a = 0; a < lines.size()-1; ++a) {
64
        if(lines[a].startsWith(QLatin1String("--- "))) {
65 66 67
            QString tail = lines[a].mid(strlen("--- "));
            if(tail.indexOf(spaceRegExp) != -1) {
                QString file = tail.left(tail.indexOf(spaceRegExp));
68
                qCDebug(PLUGIN_SVN) << "checking for" << file;
69 70 71 72
                const auto headerIt = headers.constFind(file);
                if (headerIt != headers.constEnd()) {
                    qCDebug(PLUGIN_SVN) << "adding header for" << file << ":" << *headerIt;
                    lines[a] = *headerIt + QLatin1Char('\n') + lines[a];
73 74 75 76
                }
            }
        }
    }
77
    QString ret = lines.join(QLatin1Char('\n'));
78
    qCDebug(PLUGIN_SVN) << "repaired diff:" << ret;
79 80 81
    return ret;
}

82 83 84
//@TODO: Handle raw diffs by using SvnCatJob to fetch both files/revisions

SvnInternalDiffJob::SvnInternalDiffJob( SvnJobBase* parent )
85
    : SvnInternalJobBase( parent )
86 87 88 89 90
{
    m_pegRevision.setRevisionValue( KDevelop::VcsRevision::Head,
                                    KDevelop::VcsRevision::Special );
}

91
void SvnInternalDiffJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread* /*thread*/)
92 93 94 95 96 97 98 99 100 101 102 103 104
{
    initBeforeRun();

    SvnClient cli(m_ctxt);
    try
    {

        QString diff;
        if( destination().isValid() )
        {
            QByteArray srcba;
            if( source().type() == KDevelop::VcsLocation::LocalLocation )
            {
Milian Wolff's avatar
Milian Wolff committed
105
                srcba = source().localUrl().toString( QUrl::PreferLocalFile | QUrl::StripTrailingSlash ).toUtf8();
106 107
            }else
            {
108
                srcba = source().repositoryServer().toUtf8();
109 110 111 112
            }
            QByteArray dstba;
            if( destination().type() == KDevelop::VcsLocation::LocalLocation )
            {
Milian Wolff's avatar
Milian Wolff committed
113
                dstba = destination().localUrl().toString( QUrl::PreferLocalFile | QUrl::StripTrailingSlash ).toUtf8();
114 115
            }else
            {
116
                dstba = destination().repositoryServer().toUtf8();
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
            }
            svn::Revision srcRev = createSvnCppRevisionFromVcsRevision( srcRevision() );
            svn::Revision dstRev = createSvnCppRevisionFromVcsRevision( dstRevision() );
            if( srcba.isEmpty() || ( dstba.isEmpty() && srcRev.kind() == svn_opt_revision_unspecified
                && dstRev.kind() == svn_opt_revision_unspecified ) )
            {
                throw svn::ClientException( "Not enough information for a diff");
            }
            diff = cli.diff( svn::Path( srcba.data() ), srcRev, svn::Path( dstba.data() ),
                             dstRev, recursive(), ignoreAncestry(),
                             noDiffOnDelete(), ignoreContentType() );
        }else
        {
            QByteArray srcba;
            if( source().type() == KDevelop::VcsLocation::LocalLocation )
            {
Milian Wolff's avatar
Milian Wolff committed
133
                srcba = source().localUrl().toString( QUrl::PreferLocalFile | QUrl::StripTrailingSlash ).toUtf8();
134 135
            }else
            {
136
                srcba = source().repositoryServer().toUtf8();
137 138 139 140 141 142 143 144 145 146 147 148 149 150
            }
            svn::Revision pegRev = createSvnCppRevisionFromVcsRevision( pegRevision() );
            svn::Revision srcRev = createSvnCppRevisionFromVcsRevision( srcRevision() );
            svn::Revision dstRev = createSvnCppRevisionFromVcsRevision( dstRevision() );
            if( srcba.isEmpty() || pegRev.kind() == svn_opt_revision_unspecified
                || dstRev.kind() == svn_opt_revision_unspecified
                || srcRev.kind() == svn_opt_revision_unspecified)
            {
                throw svn::ClientException( "Not enough information for a diff");
            }
            diff = cli.diff( svn::Path( srcba.data() ), pegRev, srcRev,
                             dstRev, recursive(), ignoreAncestry(),
                             noDiffOnDelete(), ignoreContentType() );
        }
151
        diff = repairDiff(diff);
152
        emit gotDiff( diff );
153

Morten Volden's avatar
Morten Volden committed
154
    }catch( const svn::ClientException& ce )
155
    {
156
        qCDebug(PLUGIN_SVN) << "Exception while doing a diff: "
157 158
                << m_source.localUrl() << m_source.repositoryServer() << m_srcRevision.prettyValue()
                << m_destination.localUrl() << m_destination.repositoryServer() << m_dstRevision.prettyValue()
159 160 161 162 163 164 165 166 167
                << QString::fromUtf8( ce.message() );
        setErrorMessage( QString::fromUtf8( ce.message() ) );
        m_success = false;
    }
}


void SvnInternalDiffJob::setSource( const KDevelop::VcsLocation& src )
{
168
    QMutexLocker l( &m_mutex );
169 170 171 172
    m_source = src;
}
void SvnInternalDiffJob::setDestination( const KDevelop::VcsLocation& dst )
{
173
    QMutexLocker l( &m_mutex );
174 175 176 177
    m_destination = dst;
}
void SvnInternalDiffJob::setSrcRevision( const KDevelop::VcsRevision& srcRev )
{
178
    QMutexLocker l( &m_mutex );
179 180 181 182
    m_srcRevision = srcRev;
}
void SvnInternalDiffJob::setDstRevision( const KDevelop::VcsRevision& dstRev )
{
183
    QMutexLocker l( &m_mutex );
184 185 186 187
    m_dstRevision = dstRev;
}
void SvnInternalDiffJob::setPegRevision( const KDevelop::VcsRevision& pegRev )
{
188
    QMutexLocker l( &m_mutex );
189 190 191 192
    m_pegRevision = pegRev;
}
void SvnInternalDiffJob::setRecursive( bool recursive )
{
193
    QMutexLocker l( &m_mutex );
194 195 196 197
    m_recursive = recursive;
}
void SvnInternalDiffJob::setIgnoreAncestry( bool ignoreAncestry )
{
198
    QMutexLocker l( &m_mutex );
199 200 201 202
    m_ignoreAncestry = ignoreAncestry;
}
void SvnInternalDiffJob::setIgnoreContentType( bool ignoreContentType )
{
203
    QMutexLocker l( &m_mutex );
204 205 206 207
    m_ignoreContentType = ignoreContentType;
}
void SvnInternalDiffJob::setNoDiffOnDelete( bool noDiffOnDelete )
{
208
    QMutexLocker l( &m_mutex );
209 210 211 212 213
    m_noDiffOnDelete = noDiffOnDelete;
}

bool SvnInternalDiffJob::recursive() const
{
214
    QMutexLocker l( &m_mutex );
215 216 217 218
    return m_recursive;
}
bool SvnInternalDiffJob::ignoreAncestry() const
{
219
    QMutexLocker l( &m_mutex );
220 221 222 223
    return m_ignoreAncestry;
}
bool SvnInternalDiffJob::ignoreContentType() const
{
224
    QMutexLocker l( &m_mutex );
225 226 227 228
    return m_ignoreContentType;
}
bool SvnInternalDiffJob::noDiffOnDelete() const
{
229
    QMutexLocker l( &m_mutex );
230 231 232 233
    return m_noDiffOnDelete;
}
KDevelop::VcsLocation SvnInternalDiffJob::source() const
{
234
    QMutexLocker l( &m_mutex );
235 236 237 238
    return m_source;
}
KDevelop::VcsLocation SvnInternalDiffJob::destination() const
{
239
    QMutexLocker l( &m_mutex );
240 241 242 243
    return m_destination;
}
KDevelop::VcsRevision SvnInternalDiffJob::srcRevision() const
{
244
    QMutexLocker l( &m_mutex );
245 246 247 248
    return m_srcRevision;
}
KDevelop::VcsRevision SvnInternalDiffJob::dstRevision() const
{
249
    QMutexLocker l( &m_mutex );
250 251 252 253
    return m_dstRevision;
}
KDevelop::VcsRevision SvnInternalDiffJob::pegRevision() const
{
254
    QMutexLocker l( &m_mutex );
255 256 257
    return m_pegRevision;
}

258
SvnDiffJob::SvnDiffJob( KDevSvnPlugin* parent )
259
    : SvnJobBaseImpl( parent, KDevelop::OutputJob::Silent )
260 261
{
    setType( KDevelop::VcsJob::Add );
262
    connect( m_job.data(), &SvnInternalDiffJob::gotDiff,
263
                this, &SvnDiffJob::setDiff, Qt::QueuedConnection );
264 265

    setObjectName(i18n("Subversion Diff"));
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
}

QVariant SvnDiffJob::fetchResults()
{
    return qVariantFromValue( m_diff );
}

void SvnDiffJob::start()
{
    if( !m_job->source().isValid()
         || ( !m_job->destination().isValid() &&
                ( m_job->srcRevision().revisionType() == KDevelop::VcsRevision::Invalid
                 || m_job->dstRevision().revisionType() == KDevelop::VcsRevision::Invalid ) )
      )
    {
281
        internalJobFailed();
282
        setErrorText( i18n( "Not enough information given to execute diff" ) );
283
    } else {
284
        startInternalJob();
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    }
}

void SvnDiffJob::setSource( const KDevelop::VcsLocation& source )
{
    if( status() == KDevelop::VcsJob::JobNotStarted )
        m_job->setSource( source );
}
void SvnDiffJob::setDestination( const KDevelop::VcsLocation& destination )
{
    if( status() == KDevelop::VcsJob::JobNotStarted )
        m_job->setDestination( destination );
}
void SvnDiffJob::setPegRevision( const KDevelop::VcsRevision& pegRevision )
{
    if( status() == KDevelop::VcsJob::JobNotStarted )
        m_job->setPegRevision( pegRevision );
}

void SvnDiffJob::setSrcRevision( const KDevelop::VcsRevision& srcRevision )
{
    if( status() == KDevelop::VcsJob::JobNotStarted )
        m_job->setSrcRevision( srcRevision );
}
void SvnDiffJob::setDstRevision( const KDevelop::VcsRevision& dstRevision )
{
    if( status() == KDevelop::VcsJob::JobNotStarted )
        m_job->setDstRevision( dstRevision );
}
void SvnDiffJob::setRecursive( bool recursive )
{
    if( status() == KDevelop::VcsJob::JobNotStarted )
        m_job->setRecursive( recursive );
}
void SvnDiffJob::setIgnoreAncestry( bool ignoreAncestry )
{
    if( status() == KDevelop::VcsJob::JobNotStarted )
        m_job->setIgnoreAncestry( ignoreAncestry );
}
void SvnDiffJob::setIgnoreContentType( bool ignoreContentType )
{
    if( status() == KDevelop::VcsJob::JobNotStarted )
        m_job->setIgnoreContentType( ignoreContentType );
}
void SvnDiffJob::setNoDiffOnDelete( bool noDiffOnDelete )
{
    if( status() == KDevelop::VcsJob::JobNotStarted )
        m_job->setNoDiffOnDelete( noDiffOnDelete );
}

void SvnDiffJob::setDiff( const QString& diff )
{
    m_diff = KDevelop::VcsDiff();
338
    m_diff.setBaseDiff(QUrl::fromLocalFile(QStringLiteral("/")));
339 340
    m_diff.setDiff( diff );

341
    emit resultsReady( this );
342
}