summaryrefslogtreecommitdiffstats
path: root/src/svnqt/client_status.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/svnqt/client_status.cpp')
-rw-r--r--src/svnqt/client_status.cpp736
1 files changed, 736 insertions, 0 deletions
diff --git a/src/svnqt/client_status.cpp b/src/svnqt/client_status.cpp
new file mode 100644
index 0000000..17f51e6
--- /dev/null
+++ b/src/svnqt/client_status.cpp
@@ -0,0 +1,736 @@
+/*
+ * Port for usage with qt-framework and development for kdesvn
+ * (C) 2005-2008 by Rajko Albrecht
+ * http://kdesvn.alwins-world.de
+ */
+/*
+ * ====================================================================
+ * Copyright (c) 2002-2005 The RapidSvn Group. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library (in the file LGPL.txt); if not,
+ * write to the Free Software Foundation, Inc., 51 Franklin St,
+ * Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * 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/.
+ * ====================================================================
+ */
+#if defined( _MSC_VER) && _MSC_VER <= 1200
+#pragma warning( disable: 4786 )// debug symbol truncated
+#endif
+
+
+// svncpp
+#include "svnqt/client_impl.hpp"
+#include "svnqt/helper.hpp"
+
+// Subversion api
+#include "svn_client.h"
+#include "svn_sorts.h"
+#include "svn_path.h"
+//#include "svn_utf.h"
+
+#include "svnqt/dirent.hpp"
+#include "svnqt/exception.hpp"
+#include "svnqt/pool.hpp"
+#include "svnqt/status.hpp"
+#include "svnqt/targets.hpp"
+#include "svnqt/info_entry.hpp"
+#include "svnqt/url.hpp"
+#include "svnqt/svnqt_defines.hpp"
+#include "svnqt/context_listener.hpp"
+
+namespace svn
+{
+
+#if ((SVN_VER_MAJOR == 1) && (SVN_VER_MINOR >= 5)) || (SVN_VER_MAJOR > 1)
+ static svn_error_t *
+ logReceiver2(
+ void*baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t * pool
+ )
+ {
+ Client_impl::sBaton * l_baton = (Client_impl::sBaton*)baton;
+ LogEntries * entries =
+ (LogEntries *) l_baton->m_data;
+ QLIST<QLONG>*rstack=
+ (QLIST<QLONG>*)l_baton->m_revstack;
+ Context*l_context = l_baton->m_context;
+ svn_client_ctx_t*ctx = l_context->ctx();
+ if (ctx&&ctx->cancel_func) {
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ }
+ if (! SVN_IS_VALID_REVNUM(log_entry->revision))
+ {
+ if (rstack&&rstack->size()>0) {
+ rstack->pop_front();
+ }
+ return SVN_NO_ERROR;
+ }
+ entries->insert (entries->begin (), LogEntry (log_entry));
+ if (rstack) {
+ entries->first().m_MergedInRevisions=(*rstack);
+ if (log_entry->has_children) {
+ rstack->push_front(log_entry->revision);
+ }
+ }
+ return SVN_NO_ERROR;
+ }
+#else
+ static svn_error_t *
+ logReceiver (
+ void *baton,
+ apr_hash_t * changedPaths,
+ svn_revnum_t rev,
+ const char *author,
+ const char *date,
+ const char *msg,
+ apr_pool_t * pool
+ )
+ {
+ Client_impl::sBaton * l_baton = (Client_impl::sBaton*)baton;
+ LogEntries * entries =
+ (LogEntries *) l_baton->m_data;
+
+ /* check every loop for cancel of operation */
+ Context*l_context = l_baton->m_context;
+ svn_client_ctx_t*ctx = l_context->ctx();
+ if (ctx&&ctx->cancel_func) {
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ }
+ entries->insert (entries->begin (), LogEntry (rev, author, date, msg));
+ if (changedPaths != NULL)
+ {
+ LogEntry &entry = entries->front ();
+
+ for (apr_hash_index_t *hi = apr_hash_first (pool, changedPaths);
+ hi != NULL;
+ hi = apr_hash_next (hi))
+ {
+ const void *pv;
+ void *val;
+ apr_hash_this (hi, &pv, NULL, &val);
+
+ svn_log_changed_path_t *log_item = reinterpret_cast<svn_log_changed_path_t *> (val);
+ const char* path = reinterpret_cast<const char*>(pv);
+
+ entry.changedPaths.push_back (
+ LogChangePathEntry (path,
+ log_item->action,
+ log_item->copyfrom_path,
+ log_item->copyfrom_rev) );
+
+ }
+ }
+
+ return SVN_NO_ERROR;
+ }
+#endif
+
+#if ((SVN_VER_MAJOR == 1) && (SVN_VER_MINOR >= 5)) || (SVN_VER_MAJOR > 1)
+ static svn_error_t *
+ logMapReceiver2(
+ void*baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t * pool
+ )
+ {
+ Client_impl::sBaton * l_baton = (Client_impl::sBaton*)baton;
+ LogEntriesMap * entries =
+ (LogEntriesMap *) l_baton->m_data;
+ Context*l_context = l_baton->m_context;
+ QLIST<QLONG>*rstack=
+ (QLIST<QLONG>*)l_baton->m_revstack;
+ svn_client_ctx_t*ctx = l_context->ctx();
+ if (ctx&&ctx->cancel_func) {
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ }
+ if (! SVN_IS_VALID_REVNUM(log_entry->revision))
+ {
+ if (rstack&&rstack->size()>0) {
+ rstack->pop_front();
+ }
+ return SVN_NO_ERROR;
+ }
+ (*entries)[log_entry->revision]=LogEntry (log_entry);
+ /// @TODO insert it into last logentry
+ if (rstack) {
+ (*entries)[log_entry->revision].m_MergedInRevisions=(*rstack);
+ if (log_entry->has_children) {
+ rstack->push_front(log_entry->revision);
+ }
+ }
+ return SVN_NO_ERROR;
+ }
+#else
+ static svn_error_t *
+ logMapReceiver (void *baton,
+ apr_hash_t * changedPaths,
+ svn_revnum_t rev,
+ const char *author,
+ const char *date,
+ const char *msg,
+ apr_pool_t * pool
+ )
+ {
+ Client_impl::sBaton * l_baton = (Client_impl::sBaton*)baton;
+ LogEntriesMap * entries =
+ (LogEntriesMap *) l_baton->m_data;
+
+ /* check every loop for cancel of operation */
+ Context*l_context = l_baton->m_context;
+ svn_client_ctx_t*ctx = l_context->ctx();
+ if (ctx&&ctx->cancel_func) {
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ }
+ (*entries)[rev]=LogEntry(rev, author, date, msg);
+ if (changedPaths != NULL)
+ {
+ LogEntry &entry = (*entries)[rev];
+
+ for (apr_hash_index_t *hi = apr_hash_first (pool, changedPaths);
+ hi != NULL;
+ hi = apr_hash_next (hi))
+ {
+ const void *pv;
+ void *val;
+ apr_hash_this (hi, &pv, NULL, &val);
+
+ svn_log_changed_path_t *log_item = reinterpret_cast<svn_log_changed_path_t *> (val);
+ const char* path = reinterpret_cast<const char*>(pv);
+
+ entry.changedPaths.push_back (
+ LogChangePathEntry (path,
+ log_item->action,
+ log_item->copyfrom_path,
+ log_item->copyfrom_rev) );
+
+ }
+ }
+
+ return NULL;
+ }
+#endif
+
+ struct StatusEntriesBaton {
+ apr_pool_t* pool;
+ apr_hash_t* hash;
+ Context*m_Context;
+ StatusEntriesBaton() {
+ pool = 0;
+ hash = 0;
+ m_Context = 0;
+ }
+ };
+
+ static svn_error_t * InfoEntryFunc(void*baton,
+ const char*path,
+ const svn_info_t*info,
+ apr_pool_t *)
+ {
+ StatusEntriesBaton* seb = (StatusEntriesBaton*)baton;
+ if (seb->m_Context) {
+ /* check every loop for cancel of operation */
+ Context*l_context = seb->m_Context;
+ svn_client_ctx_t*ctx = l_context->ctx();
+ if (ctx&&ctx->cancel_func) {
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ }
+ }
+ path = apr_pstrdup (seb->pool, path);
+ InfoEntry*e = new InfoEntry(info,path);
+ apr_hash_set (seb->hash, path, APR_HASH_KEY_STRING, e);
+ return NULL;
+ }
+
+ static void StatusEntriesFunc (void *baton,
+ const char *path,
+ svn_wc_status2_t *status)
+ {
+ svn_wc_status2_t* stat;
+ StatusEntriesBaton* seb = (StatusEntriesBaton*)baton;
+
+ path = apr_pstrdup (seb->pool, path);
+ stat = svn_wc_dup_status2 (status, seb->pool);
+ apr_hash_set (seb->hash, path, APR_HASH_KEY_STRING, stat);
+ }
+
+ static StatusEntries
+ localStatus (const Path& path,
+ Depth depth,
+ const bool get_all,
+ const bool update,
+ const bool no_ignore,
+ const bool hide_externals,
+ const StringArray & changelists,
+ Context * context)
+ {
+ svn_error_t *error;
+ StatusEntries entries;
+ apr_hash_t *status_hash;
+ svn_revnum_t revnum;
+ Revision rev (Revision::HEAD);
+ Pool pool;
+ StatusEntriesBaton baton;
+
+ status_hash = apr_hash_make (pool);
+ baton.hash = status_hash;
+ baton.pool = pool;
+#if ((SVN_VER_MAJOR == 1) && (SVN_VER_MINOR >= 5)) || (SVN_VER_MAJOR > 1)
+ error = svn_client_status3 (
+ &revnum, // revnum
+ path.path().TOUTF8(), // path
+ rev,
+ StatusEntriesFunc, // status func
+ &baton, // status baton
+ internal::DepthToSvn(depth), // see svn::Depth
+ get_all, // get all not only interesting
+ update, // check for updates
+ no_ignore, // hide ignored files or not
+ hide_externals, // hide external
+ changelists.array(pool),
+ *context, //client ctx
+ pool);
+#else
+ Q_UNUSED(changelists);
+ error = svn_client_status2 (
+ &revnum, // revnum
+ path.path().TOUTF8(), // path
+ rev,
+ StatusEntriesFunc, // status func
+ &baton, // status baton
+ (depth==DepthInfinity), //recurse
+ get_all, // get all not only interesting
+ update, // check for updates
+ no_ignore, // hide ignored files or not
+ hide_externals, // hide external
+ *context, //client ctx
+ pool);
+#endif
+
+ if (error!=NULL)
+ {
+ throw ClientException (error);
+ }
+
+ apr_array_header_t *statusarray =
+ svn_sort__hash (status_hash, svn_sort_compare_items_as_paths,
+ pool);
+ int i;
+
+ /* Loop over array, printing each name/status-structure */
+ for (i = 0; i < statusarray->nelts; ++i)
+ {
+ const svn_sort__item_t *item;
+ const char *filePath;
+ svn_wc_status2_t *status = NULL;
+
+ item = &APR_ARRAY_IDX (statusarray, i, const svn_sort__item_t);
+ status = (svn_wc_status2_t *) item->value;
+
+ filePath = (const char *) item->key;
+ entries.push_back (StatusPtr(new Status(filePath, status)));
+ }
+ return entries;
+ }
+
+ static StatusPtr
+ dirEntryToStatus (const Path& path, DirEntryPtr dirEntry)
+ {
+ QString url = path.path();
+ url += QString::FROMUTF8("/");
+ url += dirEntry->name();
+ return StatusPtr(new Status (url, dirEntry));
+ }
+
+ static StatusPtr
+ infoEntryToStatus(const Path&,const InfoEntry&infoEntry)
+ {
+ return StatusPtr(new Status(infoEntry.url(),infoEntry));
+ }
+
+ static StatusEntries
+ remoteStatus (Client * client,
+ const Path& path,
+ Depth depth,
+ const bool ,
+ const bool ,
+ const bool ,
+ const Revision revision,
+ Context * ,
+ bool detailed_remote)
+ {
+ DirEntries dirEntries = client->list(path, revision, revision, depth,detailed_remote);
+ DirEntries::const_iterator it;
+
+ StatusEntries entries;
+ QString url = path.path();
+ url+=QString::FROMUTF8("/");
+
+ for (it = dirEntries.begin (); it != dirEntries.end (); it++)
+ {
+ DirEntryPtr dirEntry = *it;
+ if (dirEntry->name().isEmpty())
+ continue;
+ entries.push_back(dirEntryToStatus (path, dirEntry));
+ }
+ return entries;
+ }
+
+ StatusEntries
+ Client_impl::status (const Path& path,
+ Depth depth,
+ const bool get_all,
+ const bool update,
+ const bool no_ignore,
+ const Revision revision,
+ bool detailed_remote,
+ const bool hide_externals,
+ const StringArray & changelists) throw (ClientException)
+ {
+ if (Url::isValid (path.path())) {
+ return remoteStatus (this, path, depth, get_all, update,
+ no_ignore,revision,m_context,detailed_remote);
+ } else {
+ return localStatus (path, depth, get_all, update,
+ no_ignore, hide_externals,changelists, m_context);
+ }
+ }
+
+ static StatusPtr
+ localSingleStatus (const Path& path, Context * context,bool update=false)
+ {
+ svn_error_t *error;
+ apr_hash_t *status_hash;
+ Pool pool;
+ StatusEntriesBaton baton;
+ svn_revnum_t revnum;
+ Revision rev (Revision::HEAD);
+
+ status_hash = apr_hash_make( pool );
+ baton.hash = status_hash;
+ baton.pool = pool;
+
+#if ((SVN_VER_MAJOR == 1) && (SVN_VER_MINOR >= 5)) || (SVN_VER_MAJOR > 1)
+ error = svn_client_status3 (
+ &revnum, // revnum
+ path.path().TOUTF8(), // path
+ rev,
+ StatusEntriesFunc, // status func
+ &baton, // status baton
+ svn_depth_empty, // not recurse
+ true, // get all not only interesting
+ update, // check for updates
+ false, // hide ignored files or not
+ false, // hide external
+ 0,
+ *context, //client ctx
+ pool);
+#else
+ error = svn_client_status2 (
+ &revnum, // revnum
+ path.path().TOUTF8(), // path
+ rev,
+ StatusEntriesFunc, // status func
+ &baton, // status baton
+ false,
+ true,
+ update,
+ false,
+ false,
+ *context, //client ctx
+ pool);
+#endif
+ if (error != NULL)
+ {
+ throw ClientException (error);
+ }
+
+ apr_array_header_t *statusarray =
+ svn_sort__hash (status_hash, svn_sort_compare_items_as_paths,
+ pool);
+ const svn_sort__item_t *item;
+ const char *filePath;
+ svn_wc_status2_t *status = NULL;
+
+ item = &APR_ARRAY_IDX (statusarray, 0, const svn_sort__item_t);
+ status = (svn_wc_status2_t *) item->value;
+ filePath = (const char *) item->key;
+
+ return StatusPtr(new Status (filePath, status));
+ };
+
+ static StatusPtr
+ remoteSingleStatus (Client * client, const Path& path,const Revision revision, Context * )
+ {
+ InfoEntries infoEntries = client->info(path,DepthEmpty,revision,Revision(Revision::UNDEFINED));
+ if (infoEntries.size () == 0)
+ return StatusPtr(new Status());
+ else
+ return infoEntryToStatus (path, infoEntries [0]);
+ }
+
+ StatusPtr
+ Client_impl::singleStatus (const Path& path,bool update,const Revision revision) throw (ClientException)
+ {
+ if (Url::isValid (path.path()))
+ return remoteSingleStatus (this, path,revision, m_context);
+ else
+ return localSingleStatus (path, m_context,update);
+ }
+
+ bool
+ Client_impl::log (const Path& path, const Revision & revisionStart,
+ const Revision & revisionEnd,
+ LogEntriesMap&log_target,
+ const Revision & revisionPeg,
+ bool discoverChangedPaths,
+ bool strictNodeHistory,int limit,
+ bool include_merged_revisions,
+ const StringArray&revprops
+ ) throw (ClientException)
+ {
+ Targets target(path);
+ Pool pool;
+ sBaton l_baton;
+ QLIST<QLONG> revstack;
+ l_baton.m_context=m_context;
+ l_baton.m_data = &log_target;
+ l_baton.m_revstack = &revstack;
+ svn_error_t *error;
+
+#if ((SVN_VER_MAJOR == 1) && (SVN_VER_MINOR >= 5)) || (SVN_VER_MAJOR > 1)
+ error = svn_client_log4 (
+ target.array (pool),
+ revisionPeg.revision(),
+ revisionStart.revision (),
+ revisionEnd.revision (),
+ limit,
+ discoverChangedPaths ? 1 : 0,
+ strictNodeHistory ? 1 : 0,
+ include_merged_revisions?1:0,
+ revprops.array(pool),
+ logMapReceiver2,
+ &l_baton,
+ *m_context, // client ctx
+ pool);
+#elif ((SVN_VER_MAJOR == 1) && (SVN_VER_MINOR >= 4)) || (SVN_VER_MAJOR > 1)
+ Q_UNUSED(include_merged_revisions);
+ Q_UNUSED(revprops);
+
+ error = svn_client_log3 (
+ target.array (pool),
+ revisionPeg.revision(),
+ revisionStart.revision (),
+ revisionEnd.revision (),
+ limit,
+ discoverChangedPaths ? 1 : 0,
+ strictNodeHistory ? 1 : 0,
+ logMapReceiver,
+ &l_baton,
+ *m_context, // client ctx
+ pool);
+#else
+ Q_UNUSED(include_merged_revisions);
+ Q_UNUSED(revprops);
+ Q_UNUSED(revisionPeg);
+
+ error = svn_client_log2 (
+ target.array (pool),
+ revisionStart.revision (),
+ revisionEnd.revision (),
+ limit,
+ discoverChangedPaths ? 1 : 0,
+ strictNodeHistory ? 1 : 0,
+ logMapReceiver,
+ &l_baton,
+ *m_context, // client ctx
+ pool);
+#endif
+ if (error != NULL)
+ {
+ throw ClientException (error);
+ }
+ return true;
+ }
+
+ LogEntriesPtr
+ Client_impl::log (const Path& path, const Revision & revisionStart,
+ const Revision & revisionEnd, const Revision & revisionPeg,
+ bool discoverChangedPaths,
+ bool strictNodeHistory,int limit,
+ bool include_merged_revisions,
+ const StringArray&revprops
+ ) throw (ClientException)
+ {
+ Targets target(path);
+ Pool pool;
+ LogEntriesPtr entries = LogEntriesPtr(new LogEntries ());
+ QLIST<QLONG> revstack;
+ sBaton l_baton;
+ l_baton.m_context=m_context;
+ l_baton.m_data = entries;
+ l_baton.m_revstack = &revstack;
+
+ svn_error_t *error;
+#if ((SVN_VER_MAJOR == 1) && (SVN_VER_MINOR >= 5)) || (SVN_VER_MAJOR > 1)
+ error = svn_client_log4 (
+ target.array (pool),
+ revisionPeg.revision(),
+ revisionStart.revision (),
+ revisionEnd.revision (),
+ limit,
+ discoverChangedPaths ? 1 : 0,
+ strictNodeHistory ? 1 : 0,
+ include_merged_revisions?1:0,
+ revprops.array(pool),
+ logReceiver2,
+ &l_baton,
+ *m_context, // client ctx
+ pool);
+#elif ((SVN_VER_MAJOR == 1) && (SVN_VER_MINOR >= 4)) || (SVN_VER_MAJOR > 1)
+ Q_UNUSED(include_merged_revisions);
+ Q_UNUSED(revprops);
+
+ error = svn_client_log3 (
+ target.array (pool),
+ revisionPeg.revision(),
+ revisionStart.revision (),
+ revisionEnd.revision (),
+ limit,
+ discoverChangedPaths ? 1 : 0,
+ strictNodeHistory ? 1 : 0,
+ logReceiver,
+ &l_baton,
+ *m_context, // client ctx
+ pool);
+#else
+ Q_UNUSED(include_merged_revisions);
+ Q_UNUSED(revprops);
+ Q_UNUSED(revisionPeg);
+
+ error = svn_client_log2 (
+ target.array (pool),
+ revisionStart.revision (),
+ revisionEnd.revision (),
+ limit,
+ discoverChangedPaths ? 1 : 0,
+ strictNodeHistory ? 1 : 0,
+ logReceiver,
+ &l_baton,
+ *m_context, // client ctx
+ pool);
+#endif
+ if (error != NULL)
+ {
+ throw ClientException (error);
+ }
+
+ return entries;
+ }
+
+ InfoEntries
+ Client_impl::info(const Path& _p,
+ Depth depth,
+ const Revision & rev,
+ const Revision & peg_revision,
+ const StringArray&changelists
+ ) throw (ClientException)
+ {
+
+ InfoEntries ientries;
+ Pool pool;
+ svn_error_t *error = NULL;
+ StatusEntriesBaton baton;
+ apr_hash_t *status_hash;
+
+ status_hash = apr_hash_make (pool);
+ baton.hash = status_hash;
+ baton.pool = pool;
+ baton.m_Context=m_context;
+ svn_opt_revision_t pegr;
+ const char *truepath = 0;
+ bool internal_peg = false;
+ QByteArray _buf = _p.cstr();
+
+ error = svn_opt_parse_path(&pegr, &truepath,
+ _buf,
+ pool);
+ if (error != NULL)
+ throw ClientException (error);
+
+ if (peg_revision.kind() == svn_opt_revision_unspecified) {
+ if ((svn_path_is_url (_p.cstr())) && (pegr.kind == svn_opt_revision_unspecified)) {
+ pegr.kind = svn_opt_revision_head;
+ internal_peg=true;
+ }
+ }
+
+#if ((SVN_VER_MAJOR == 1) && (SVN_VER_MINOR >= 5)) || (SVN_VER_MAJOR > 1)
+ error =
+ svn_client_info2(truepath,
+ internal_peg?&pegr:peg_revision.revision(),
+ rev.revision (),
+ &InfoEntryFunc,
+ &baton,
+ internal::DepthToSvn(depth),
+ changelists.array(pool),
+ *m_context, //client ctx
+ pool);
+#else
+ bool rec = depth==DepthInfinity;
+ Q_UNUSED(changelists);
+ error =
+ svn_client_info(truepath,
+ internal_peg?&pegr:peg_revision.revision(),
+ rev.revision (),
+ &InfoEntryFunc,
+ &baton,
+ rec,
+ *m_context, //client ctx
+ pool);
+#endif
+
+ if (error != NULL)
+ throw ClientException (error);
+
+ apr_array_header_t *statusarray =
+ svn_sort__hash (status_hash, svn_sort_compare_items_as_paths,
+ pool);
+ int i;
+
+ /* Loop over array, printing each name/status-structure */
+ for (i=0; i< statusarray->nelts; ++i)
+ {
+ const svn_sort__item_t *item;
+ InfoEntry*e = NULL;
+ item = &APR_ARRAY_IDX (statusarray, i, const svn_sort__item_t);
+ e = (InfoEntry *) item->value;
+ ientries.push_back(*e);
+ delete e;
+ }
+ if (error != NULL)
+ throw ClientException (error);
+ return ientries;
+ }
+
+}
+
+/* -----------------------------------------------------------------
+ * local variables:
+ * eval: (load-file "../../rapidsvn-dev.el")
+ * end:
+ */