diff --git a/plugins/subversion/kdevsvncpp/context.cpp b/plugins/subversion/kdevsvncpp/context.cpp index a90cdd7eb3..703ce81e7d 100644 --- a/plugins/subversion/kdevsvncpp/context.cpp +++ b/plugins/subversion/kdevsvncpp/context.cpp @@ -1,714 +1,722 @@ /* * ==================================================================== * Copyright (c) 2002-2009 The RapidSvn Group. All rights reserved. * * 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 3 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 (in the file GPL.txt. * If not, see . * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://rapidsvn.tigris.org/. * ==================================================================== */ /** * @todo implement 1.3 SVN api: * * SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN * svn_client_add3 * svn_client_copy2 * svn_client_commit3 * svn_client_delete2 * svn_client_move3 * svn_client_mkdir2 * svn_client_import2 * svn_client_info */ // Apache Portable Runtime #include "apr_xlate.h" // Subversion api #include "svn_auth.h" #include "svn_config.h" #include "svn_subst.h" //#include "svn_utf.h" // svncpp #include "kdevsvncpp/apr.hpp" #include "kdevsvncpp/context.hpp" #include "kdevsvncpp/context_listener.hpp" namespace svn { struct Context::Data { public: /** The usage of Apr makes sure Apr is initialized * before any use of apr functions. */ Apr apr; ContextListener * listener; bool logIsSet; int promptCounter; Pool pool; svn_client_ctx_t * ctx; std::string username; std::string password; std::string logMessage; std::string configDir; /** * translate native c-string to utf8 */ static svn_error_t * translateString(const char * str, const char ** newStr, apr_pool_t * /*pool*/) { // due to problems with apr_xlate we dont perform // any conversion at this place. YOU will have to make // sure any strings passed are UTF 8 strings // svn_string_t *string = svn_string_create ("", pool); // // string->data = str; // string->len = strlen (str); // // const char * encoding = APR_LOCALE_CHARSET; // // SVN_ERR (svn_subst_translate_string (&string, string, // encoding, pool)); // // *newStr = string->data; *newStr = str; return SVN_NO_ERROR; } /** * the @a baton is interpreted as Data * * Several checks are performed on the baton: * - baton == 0? * - baton->Data * - listener set? * * @param baton * @param data returned data if everything is OK * @retval SVN_NO_ERROR if everything is fine * @retval SVN_ERR_CANCELLED on invalid values */ static svn_error_t * getData(void * baton, Data ** data) { if (baton == NULL) return svn_error_create(SVN_ERR_CANCELLED, NULL, "invalid baton"); Data * data_ = static_cast (baton); if (data_->listener == 0) return svn_error_create(SVN_ERR_CANCELLED, NULL, "invalid listener"); *data = data_; return SVN_NO_ERROR; } Data(const std::string & configDir_) : listener(0), logIsSet(false), promptCounter(0), configDir(configDir_) { const char * c_configDir = 0; if (configDir.length() > 0) c_configDir = configDir.c_str(); // make sure the configuration directory exists svn_config_ensure(c_configDir, pool); // initialize authentication providers // * simple // * username // * simple prompt // * ssl server trust file // * ssl server trust prompt // * ssl client cert pw file // * ssl client cert pw prompt // * ssl client cert file // =================== // 8 providers apr_array_header_t *providers = apr_array_make(pool, 8, sizeof(svn_auth_provider_object_t *)); svn_auth_provider_object_t *provider; svn_client_get_simple_provider( &provider, pool); *(svn_auth_provider_object_t **)apr_array_push(providers) = provider; svn_client_get_username_provider( &provider, pool); *(svn_auth_provider_object_t **)apr_array_push(providers) = provider; svn_client_get_simple_prompt_provider( &provider, onSimplePrompt, this, 100000000, // not very nice. should be infinite... pool); *(svn_auth_provider_object_t **)apr_array_push(providers) = provider; // add ssl providers // file first then prompt providers svn_client_get_ssl_server_trust_file_provider(&provider, pool); *(svn_auth_provider_object_t **)apr_array_push(providers) = provider; svn_client_get_ssl_client_cert_file_provider(&provider, pool); *(svn_auth_provider_object_t **)apr_array_push(providers) = provider; svn_client_get_ssl_client_cert_pw_file_provider(&provider, pool); *(svn_auth_provider_object_t **)apr_array_push(providers) = provider; svn_client_get_ssl_server_trust_prompt_provider( &provider, onSslServerTrustPrompt, this, pool); *(svn_auth_provider_object_t **)apr_array_push(providers) = provider; // plugged in 3 as the retry limit - what is a good limit? svn_client_get_ssl_client_cert_pw_prompt_provider( &provider, onSslClientCertPwPrompt, this, 3, pool); *(svn_auth_provider_object_t **)apr_array_push(providers) = provider; svn_auth_baton_t *ab; svn_auth_open(&ab, providers, pool); // initialize ctx structure svn_client_create_context(&ctx, pool); // get the config based on the configDir passed in svn_config_get_config(&ctx->config, c_configDir, pool); + // disable external diff and diff3 commands + svn_config_t *config = (svn_config_t *)apr_hash_get( + ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING); + svn_config_set(config, SVN_CONFIG_SECTION_HELPERS, + SVN_CONFIG_OPTION_DIFF_CMD, NULL); + svn_config_set(config, SVN_CONFIG_SECTION_HELPERS, + SVN_CONFIG_OPTION_DIFF3_CMD, NULL); + // tell the auth functions where the config is svn_auth_set_parameter(ab, SVN_AUTH_PARAM_CONFIG_DIR, c_configDir); ctx->auth_baton = ab; ctx->log_msg_func = onLogMsg; ctx->log_msg_baton = this; ctx->notify_func = onNotify; ctx->notify_baton = this; ctx->cancel_func = onCancel; ctx->cancel_baton = this; #if (SVN_VER_MAJOR >= 1) && (SVN_VER_MINOR >= 2) ctx->notify_func2 = onNotify2; ctx->notify_baton2 = this; #endif } void setAuthCache(bool value) { void *param = 0; if (!value) param = (void *)"1"; svn_auth_set_parameter(ctx->auth_baton, SVN_AUTH_PARAM_NO_AUTH_CACHE, param); } /** @see Context::setLogin */ void setLogin(const char * usr, const char * pwd) { username = usr; password = pwd; svn_auth_baton_t * ab = ctx->auth_baton; svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_USERNAME, username.c_str()); svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD, password.c_str()); } /** @see Context::setLogMessage */ void setLogMessage(const char * msg) { logMessage = msg; logIsSet = true; } /** * this function gets called by the subversion api function * when a log message is needed. This is the case on a commit * for example */ static svn_error_t * onLogMsg(const char **log_msg, const char **tmp_file, apr_array_header_t *, //UNUSED commit_items void *baton, apr_pool_t * pool) { Data * data = NULL; SVN_ERR(getData(baton, &data)); std::string msg; if (data->logIsSet) msg = data->getLogMessage(); else { if (!data->retrieveLogMessage(msg)) return svn_error_create(SVN_ERR_CANCELLED, NULL, ""); } *log_msg = apr_pstrdup(pool, msg.c_str()); *tmp_file = NULL; return SVN_NO_ERROR; } /** * this is the callback function for the subversion * api functions to signal the progress of an action */ static void onNotify(void * baton, const char *path, svn_wc_notify_action_t action, svn_node_kind_t kind, const char *mime_type, svn_wc_notify_state_t content_state, svn_wc_notify_state_t prop_state, svn_revnum_t revision) { if (baton == 0) return; Data * data = static_cast (baton); data->notify(path, action, kind, mime_type, content_state, prop_state, revision); } #if (SVN_VER_MAJOR >= 1) && (SVN_VER_MINOR >= 2) /** * this is the callback function for the subversion 1.2 * api functions to signal the progress of an action * * @todo right now we forward only to @a onNotify, * but maybe we should a notify2 to the listener * @since subversion 1.2 */ static void onNotify2(void*baton,const svn_wc_notify_t *action,apr_pool_t *) { if (!baton) return; // for now forward the call to @a onNotify onNotify(baton, action->path, action->action, action->kind, action->mime_type, action->content_state, action->prop_state, action->revision); } #endif /** * this is the callback function for the subversion * api functions to signal the progress of an action */ static svn_error_t * onCancel(void * baton) { if (baton == 0) return SVN_NO_ERROR; Data * data = static_cast (baton); if (data->cancel()) return svn_error_create(SVN_ERR_CANCELLED, NULL, "cancelled by user"); else return SVN_NO_ERROR; } /** * @see svn_auth_simple_prompt_func_t */ static svn_error_t * onSimplePrompt(svn_auth_cred_simple_t **cred, void *baton, const char *realm, const char *username, svn_boolean_t _may_save, apr_pool_t *pool) { Data * data = NULL; SVN_ERR(getData(baton, &data)); bool may_save = _may_save != 0; if (!data->retrieveLogin(username, realm, may_save)) return svn_error_create(SVN_ERR_CANCELLED, NULL, ""); svn_auth_cred_simple_t* lcred = (svn_auth_cred_simple_t*) apr_palloc(pool, sizeof(svn_auth_cred_simple_t)); /* SVN_ERR (svn_utf_cstring_to_utf8 ( &lcred->password, data->getPassword (), pool)); SVN_ERR (svn_utf_cstring_to_utf8 ( &lcred->username, data->getUsername (), pool)); */ lcred->password = data->getPassword(); lcred->username = data->getUsername(); // tell svn if the credentials need to be saved lcred->may_save = may_save; *cred = lcred; return SVN_NO_ERROR; } /** * @see svn_auth_ssl_server_trust_prompt_func_t */ static svn_error_t * onSslServerTrustPrompt(svn_auth_cred_ssl_server_trust_t **cred, void *baton, const char *realm, apr_uint32_t failures, const svn_auth_ssl_server_cert_info_t *info, svn_boolean_t may_save, apr_pool_t *pool) { Data * data = NULL; SVN_ERR(getData(baton, &data)); ContextListener::SslServerTrustData trustData(failures); if (realm != NULL) trustData.realm = realm; trustData.hostname = info->hostname; trustData.fingerprint = info->fingerprint; trustData.validFrom = info->valid_from; trustData.validUntil = info->valid_until; trustData.issuerDName = info->issuer_dname; trustData.maySave = may_save != 0; apr_uint32_t acceptedFailures; ContextListener::SslServerTrustAnswer answer = data->listener->contextSslServerTrustPrompt( trustData, acceptedFailures); if (answer == ContextListener::DONT_ACCEPT) *cred = NULL; else { svn_auth_cred_ssl_server_trust_t *cred_ = (svn_auth_cred_ssl_server_trust_t*) apr_palloc(pool, sizeof(svn_auth_cred_ssl_server_trust_t)); if (answer == ContextListener::ACCEPT_PERMANENTLY) { cred_->may_save = 1; cred_->accepted_failures = acceptedFailures; } *cred = cred_; } return SVN_NO_ERROR; } /** * @see svn_auth_ssl_client_cert_prompt_func_t */ static svn_error_t * onSslClientCertPrompt(svn_auth_cred_ssl_client_cert_t **cred, void *baton, apr_pool_t *pool) { Data * data = NULL; SVN_ERR(getData(baton, &data)); std::string certFile; if (!data->listener->contextSslClientCertPrompt(certFile)) return svn_error_create(SVN_ERR_CANCELLED, NULL, ""); svn_auth_cred_ssl_client_cert_t *cred_ = (svn_auth_cred_ssl_client_cert_t*) apr_palloc(pool, sizeof(svn_auth_cred_ssl_client_cert_t)); /* SVN_ERR (svn_utf_cstring_to_utf8 ( &cred_->cert_file, certFile.c_str (), pool)); */ cred_->cert_file = certFile.c_str(); *cred = cred_; return SVN_NO_ERROR; } /** * @see svn_auth_ssl_client_cert_pw_prompt_func_t */ static svn_error_t * onSslClientCertPwPrompt( svn_auth_cred_ssl_client_cert_pw_t **cred, void *baton, const char *realm, svn_boolean_t maySave, apr_pool_t *pool) { Data * data = NULL; SVN_ERR(getData(baton, &data)); std::string password; bool may_save = maySave != 0; if (!data->listener->contextSslClientCertPwPrompt(password, realm, may_save)) return svn_error_create(SVN_ERR_CANCELLED, NULL, ""); svn_auth_cred_ssl_client_cert_pw_t *cred_ = (svn_auth_cred_ssl_client_cert_pw_t *) apr_palloc(pool, sizeof(svn_auth_cred_ssl_client_cert_pw_t)); /* SVN_ERR (svn_utf_cstring_to_utf8 ( &cred_->password, password.c_str (), pool)); */ cred_->password = password.c_str(); cred_->may_save = may_save; *cred = cred_; return SVN_NO_ERROR; } const char * getUsername() const { return username.c_str(); } const char * getPassword() const { return password.c_str(); } const char * getLogMessage() const { return logMessage.c_str(); } /** * if the @a listener is set, use it to retrieve the log * message using ContextListener::contextGetLogMessage. * This return values is given back, then. * * if the @a listener is not set the its checked whether * the log message has been set using @a setLogMessage * yet. If not, return false otherwise true * * @param msg log message * @retval false cancel */ bool retrieveLogMessage(std::string & msg) { bool ok; if (listener == 0) return false; ok = listener->contextGetLogMessage(logMessage); if (ok) msg = logMessage; else logIsSet = false; return ok; } /** * if the @a listener is set and no password has been * set yet, use it to retrieve login and password using * ContextListener::contextGetLogin. * * if the @a listener is not set, check if setLogin * has been called yet. * * @return continue? * @retval false cancel */ bool retrieveLogin(const char * username_, const char * realm, bool &may_save) { bool ok; if (listener == 0) return false; if (username_ == NULL) username = ""; else username = username_; ok = listener->contextGetLogin(realm, username, password, may_save); return ok; } /** * if the @a listener is set call the method * @a contextNotify */ void notify(const char *path, svn_wc_notify_action_t action, svn_node_kind_t kind, const char *mime_type, svn_wc_notify_state_t content_state, svn_wc_notify_state_t prop_state, svn_revnum_t revision) { if (listener != 0) { listener->contextNotify(path, action, kind, mime_type, content_state, prop_state, revision); } } /** * if the @a listener is set call the method * @a contextCancel */ bool cancel() { if (listener != 0) { return listener->contextCancel(); } else { // don't cancel if no listener return false; } } }; Context::Context(const std::string &configDir) { m = new Data(configDir); } Context::Context(const Context & src) { m = new Data(src.m->configDir); setLogin(src.getUsername(), src.getPassword()); } Context::~Context() { delete m; } void Context::setAuthCache(bool value) { m->setAuthCache(value); } void Context::setLogin(const char * username, const char * password) { m->setLogin(username, password); } Context::operator svn_client_ctx_t * () { return m->ctx; } svn_client_ctx_t * Context::ctx() { return m->ctx; } void Context::setLogMessage(const char * msg) { m->setLogMessage(msg); } const char * Context::getUsername() const { return m->getUsername(); } const char * Context::getPassword() const { return m->getPassword(); } const char * Context::getLogMessage() const { return m->getLogMessage(); } void Context::setListener(ContextListener * listener) { m->listener = listener; } ContextListener * Context::getListener() const { return m->listener; } void Context::reset() { m->promptCounter = 0; m->logIsSet = false; } } /* ----------------------------------------------------------------- * local variables: * eval: (load-file "../../rapidsvn-dev.el") * end: */ diff --git a/plugins/subversion/svndiffjob.cpp b/plugins/subversion/svndiffjob.cpp index 7cd799b196..b722a7f276 100644 --- a/plugins/subversion/svndiffjob.cpp +++ b/plugins/subversion/svndiffjob.cpp @@ -1,480 +1,479 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * 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 #include #include #include #include #include #include #include #include #include "kdevsvncpp/path.hpp" #include "kdevsvncpp/revision.hpp" #include "icore.h" #include "iruncontroller.h" #include "svnclient.h" #include "svncatjob.h" ///@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. QString repairDiff(QString diff) { kDebug() << "diff before repair:" << diff; QStringList lines = diff.split('\n'); QMap headers; for(int a = 0; a < lines.size()-1; ++a) { if(lines[a].startsWith("Index: ") && lines[a+1].startsWith("=====")) { QString fileName = lines[a].mid(strlen("Index: ")).trimmed(); headers[fileName] = lines[a]; kDebug() << "found header for" << fileName; lines[a] = QString(); if(lines[a+1].startsWith("======")) { headers[fileName] += '\n' + lines[a+1]; lines[a+1] = QString(); } } } QRegExp spaceRegExp("\\s"); for(int a = 0; a < lines.size()-1; ++a) { if(lines[a].startsWith("--- ")) { QString tail = lines[a].mid(strlen("--- ")); if(tail.indexOf(spaceRegExp) != -1) { QString file = tail.left(tail.indexOf(spaceRegExp)); kDebug() << "checking for" << file; if(headers.contains(file)) { kDebug() << "adding header for" << file << ":" << headers[file]; lines[a] = headers[file] + '\n' + lines[a]; } } } } QString ret = lines.join("\n"); kDebug() << "repaired diff:" << ret; return ret; } //@TODO: Handle raw diffs by using SvnCatJob to fetch both files/revisions SvnInternalDiffJob::SvnInternalDiffJob( SvnJobBase* parent ) : SvnInternalJobBase( parent ), m_recursive( true ), m_ignoreAncestry( false ), m_ignoreContentType( false ), m_noDiffOnDelete( false ) { m_pegRevision.setRevisionValue( KDevelop::VcsRevision::Head, KDevelop::VcsRevision::Special ); } void SvnInternalDiffJob::run() { initBeforeRun(); SvnClient cli(m_ctxt); try { QString diff; if( destination().isValid() ) { QByteArray srcba; if( source().type() == KDevelop::VcsLocation::LocalLocation ) { KUrl url = source().localUrl(); if( url.isLocalFile() ) { srcba = url.toLocalFile( KUrl::RemoveTrailingSlash ).toUtf8(); }else { srcba = url.url( KUrl::RemoveTrailingSlash ).toUtf8(); } }else { srcba = source().repositoryServer().toUtf8(); } QByteArray dstba; if( destination().type() == KDevelop::VcsLocation::LocalLocation ) { KUrl url = destination().localUrl(); if( url.isLocalFile() ) { dstba = url.toLocalFile( KUrl::RemoveTrailingSlash ).toUtf8(); }else { dstba = url.url().toUtf8(); } }else { dstba = destination().repositoryServer().toUtf8(); } 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 ) { KUrl url = source().localUrl(); if( url.isLocalFile() ) { srcba = url.toLocalFile( KUrl::RemoveTrailingSlash ).toUtf8(); }else { srcba = url.url().toUtf8(); } }else { srcba = source().repositoryServer().toUtf8(); } 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"); } - //@TODO: Make sure there's no diff-cmd set via the users configuration file, can be done only via C api diff = cli.diff( svn::Path( srcba.data() ), pegRev, srcRev, dstRev, recursive(), ignoreAncestry(), noDiffOnDelete(), ignoreContentType() ); } diff = repairDiff(diff); emit gotDiff( diff ); }catch( svn::ClientException ce ) { kDebug(9510) << "Exception while doing a diff: " << m_source.localUrl() << m_source.repositoryServer() << m_srcRevision.prettyValue() << m_destination.localUrl() << m_destination.repositoryServer() << m_dstRevision.prettyValue() << QString::fromUtf8( ce.message() ); setErrorMessage( QString::fromUtf8( ce.message() ) ); m_success = false; } } void SvnInternalDiffJob::setSource( const KDevelop::VcsLocation& src ) { QMutexLocker l( m_mutex ); m_source = src; } void SvnInternalDiffJob::setDestination( const KDevelop::VcsLocation& dst ) { QMutexLocker l( m_mutex ); m_destination = dst; } void SvnInternalDiffJob::setSrcRevision( const KDevelop::VcsRevision& srcRev ) { QMutexLocker l( m_mutex ); m_srcRevision = srcRev; } void SvnInternalDiffJob::setDstRevision( const KDevelop::VcsRevision& dstRev ) { QMutexLocker l( m_mutex ); m_dstRevision = dstRev; } void SvnInternalDiffJob::setPegRevision( const KDevelop::VcsRevision& pegRev ) { QMutexLocker l( m_mutex ); m_pegRevision = pegRev; } void SvnInternalDiffJob::setRecursive( bool recursive ) { QMutexLocker l( m_mutex ); m_recursive = recursive; } void SvnInternalDiffJob::setIgnoreAncestry( bool ignoreAncestry ) { QMutexLocker l( m_mutex ); m_ignoreAncestry = ignoreAncestry; } void SvnInternalDiffJob::setIgnoreContentType( bool ignoreContentType ) { QMutexLocker l( m_mutex ); m_ignoreContentType = ignoreContentType; } void SvnInternalDiffJob::setNoDiffOnDelete( bool noDiffOnDelete ) { QMutexLocker l( m_mutex ); m_noDiffOnDelete = noDiffOnDelete; } bool SvnInternalDiffJob::recursive() const { QMutexLocker l( m_mutex ); return m_recursive; } bool SvnInternalDiffJob::ignoreAncestry() const { QMutexLocker l( m_mutex ); return m_ignoreAncestry; } bool SvnInternalDiffJob::ignoreContentType() const { QMutexLocker l( m_mutex ); return m_ignoreContentType; } bool SvnInternalDiffJob::noDiffOnDelete() const { QMutexLocker l( m_mutex ); return m_noDiffOnDelete; } KDevelop::VcsLocation SvnInternalDiffJob::source() const { QMutexLocker l( m_mutex ); return m_source; } KDevelop::VcsLocation SvnInternalDiffJob::destination() const { QMutexLocker l( m_mutex ); return m_destination; } KDevelop::VcsRevision SvnInternalDiffJob::srcRevision() const { QMutexLocker l( m_mutex ); return m_srcRevision; } KDevelop::VcsRevision SvnInternalDiffJob::dstRevision() const { QMutexLocker l( m_mutex ); return m_dstRevision; } KDevelop::VcsRevision SvnInternalDiffJob::pegRevision() const { QMutexLocker l( m_mutex ); return m_pegRevision; } SvnDiffJob::SvnDiffJob( KDevSvnPlugin* parent ) : SvnJobBase( parent, KDevelop::OutputJob::Silent ) { setType( KDevelop::VcsJob::Add ); m_job = new SvnInternalDiffJob( this ); setObjectName(i18n("Subversion Diff")); } QVariant SvnDiffJob::fetchResults() { return qVariantFromValue( m_diff ); } void SvnDiffJob::start() { disconnect( m_job, SIGNAL(done(ThreadWeaver::Job*)), this, SLOT(internalJobDone(ThreadWeaver::Job*)) ); if( !m_job->source().isValid() || ( !m_job->destination().isValid() && ( m_job->srcRevision().revisionType() == KDevelop::VcsRevision::Invalid || m_job->dstRevision().revisionType() == KDevelop::VcsRevision::Invalid ) ) ) { internalJobFailed( m_job ); setErrorText( i18n( "Not enough information given to execute diff" ) ); }else { connect( m_job, SIGNAL(gotDiff(QString)), this, SLOT(setDiff(QString)), Qt::QueuedConnection ); ThreadWeaver::Weaver::instance()->enqueue( m_job ); } } SvnInternalJobBase* SvnDiffJob::internalJob() const { return m_job; } 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(); m_diff.setBaseDiff(KUrl("/")); m_diff.setType( KDevelop::VcsDiff::DiffUnified ); m_diff.setContentType( KDevelop::VcsDiff::Text ); m_diff.setDiff( diff ); QRegExp fileRe("(?:^|\n)Index: ([^\n]+)\n"); QStringList paths; int pos = 0; while( ( pos = fileRe.indexIn( diff, pos ) ) != -1 ) { paths << fileRe.cap(1); pos += fileRe.matchedLength(); } if (paths.isEmpty()) { internalJobDone( m_job ); emit resultsReady( this ); return; } foreach( const QString &s, paths ) { if( !s.isEmpty() ) { SvnCatJob* job = new SvnCatJob( m_part ); KDevelop::VcsLocation l = m_job->source(); if( l.type() == KDevelop::VcsLocation::LocalLocation ) { l.setLocalUrl( KUrl( s ) ); }else { QString repoLocation = KUrl( l.repositoryServer() ).toLocalFile( KUrl::RemoveTrailingSlash ); QFileInfo fi( repoLocation ); if( s == fi.fileName() ) { l.setRepositoryServer( l.repositoryServer() ); }else { l.setRepositoryServer( l.repositoryServer() + '/' + s ); } } job->setSource( l ); job->setPegRevision( m_job->pegRevision() ); job->setSrcRevision( m_job->srcRevision() ); m_catJobMap[job] = l; connect( job, SIGNAL(resultsReady(KDevelop::VcsJob*)), this, SLOT(addLeftText(KDevelop::VcsJob*)) ); connect( job, SIGNAL(result(KJob*)), this, SLOT(removeJob(KJob*)) ); KDevelop::ICore::self()->runController()->registerJob(job); } } } void SvnDiffJob::addLeftText( KDevelop::VcsJob* job ) { if( m_catJobMap.contains( job ) ) { QVariant v = job->fetchResults(); m_diff.addLeftText( m_catJobMap[job], v.toString() ); m_catJobMap.remove(job); // KJobs delete themselves when finished } if( m_catJobMap.isEmpty() ) { internalJobDone( m_job ); emit resultsReady( this ); } } void SvnDiffJob::removeJob( KJob* job ) { if( job->error() != 0 ) { KDevelop::VcsJob* j = dynamic_cast( job ); if( j ) { if( m_catJobMap.contains( j ) ) { m_catJobMap.remove(j); // KJobs delete themselves when finished } } } if( m_catJobMap.isEmpty() ) { internalJobDone( m_job ); emit resultsReady( this ); } } void SvnDiffJob::setDiffType( KDevelop::VcsDiff::Type type ) { m_diffType = type; } #include "svndiffjob.moc" #include "svndiffjob_p.moc"