/*
* ====================================================================
* Copyright (c) 2002-2005 The RapidSvn Group. All rights reserved.
*
* This software is licensed as described in the file LICENSE.txt,
* which you should have received as part of this distribution.
*
* 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
// stl
#include <string>
// subversion api
#include "svn_client.h"
// subcpp
#include "client.hpp"
#include "exception.hpp"
#include "pool.hpp"
#include "targets.hpp"
namespace svn
{
svn_revnum_t Client::Checkout( const Path & url,
const Path & destPath,
const Revision & pegRevision,
const Revision & revision,
svn_depth_t depth,
bool ignoreExternals,
bool allowUnversionedObstructions )
{
Pool subPool;
apr_pool_t * apr_pool = subPool.pool();
svn_revnum_t revnum = 0;
svn_error_t * error =
svn_client_checkout3( &revnum,
url.c_str(), destPath.c_str(),
pegRevision.revision(),
revision.revision(),
depth,
ignoreExternals,
allowUnversionedObstructions,
*m_context,
apr_pool );
if( error != NULL )
throw ClientException( error );
return revnum;
}
void Client::Delete( const Path & path,
const std::string& message,
bool force,
bool keep_local )
{
Targets targets( path.c_str() );
Delete( targets, message, force, keep_local );
}
void Client::Delete( const Targets & targets,
const std::string& message,
bool force,
bool keep_local )
{
Pool pool;
svn_commit_info_t *commit_info = NULL;
m_context->SetLogMessage( message );
svn_error_t * error = svn_client_delete3( &commit_info,
const_cast<apr_array_header_t*>( targets.array( pool ) ),
force,
keep_local,
NULL, /* TODO: Support revprops */
*m_context,
pool );
m_context->ClearLogMessage();
if( error != NULL )
{
throw ClientException( error );
}
}
void Client::Revert( const Targets & targets,
svn_depth_t depth /*= svn_depth_unknown*/ )
{
Pool pool;
svn_error_t * error =
svn_client_revert2( ( targets.array( pool ) ),
depth,
NULL, // TODO: Support changelists
*m_context,
pool );
if( error != NULL )
throw ClientException( error );
}
void Client::Add( const Path & path,
svn_depth_t depth /*= svn_depth_unknown*/,
bool force /*= false*/,
bool no_ignore /*= false*/,
bool add_parents /*= false*/ )
{
Pool pool;
svn_error_t * error =
svn_client_add4( path.c_str(),
depth,
force,
no_ignore,
add_parents,
*m_context,
pool );
if( error != NULL )
throw ClientException( error );
}
std::vector< svn_revnum_t > Client::Update( const Targets & paths,
const Revision & revision,
svn_depth_t depth,
bool depthIsSticky,
bool ignoreExternals,
bool allowUnverObstructions )
{
Pool pool;
apr_array_header_t* revResults;
svn_error_t * error = svn_client_update3( &revResults,
paths.array( pool ),
revision.revision(),
depth,
depthIsSticky,
ignoreExternals,
allowUnverObstructions,
*m_context,
pool );
if( error != NULL )
{
throw ClientException( error );
}
// Convert apr array to vector
std::vector< svn_revnum_t > revs;
revs.reserve( revResults->nelts );
for( int i = 0; i < revResults->nelts; i++ )
{
svn_revnum_t* revNum = &APR_ARRAY_IDX( revResults, i, svn_revnum_t );
revs.push_back( *revNum );
}
return revs;
}
svn_revnum_t Client::Commit( const Targets & targets,
const std::string& message,
svn_depth_t depth,
bool keepLocks,
bool keepChangelists )
{
Pool pool;
m_context->SetLogMessage( message );
svn_commit_info_t *commit_info = NULL;
svn_error_t * error = svn_client_commit4( &commit_info,
targets.array( pool ),
depth,
keepLocks,
keepChangelists,
NULL, /* TODO: Support changelists */
NULL, /* TODO: Support revprops */
*m_context,
pool );
m_context->ClearLogMessage();
if( error != NULL )
throw ClientException( error );
if( commit_info )
return commit_info->revision;
return SVN_INVALID_REVNUM;
}
void Client::Copy( const Path & srcPath,
const Revision & srcRevision,
const Path & destPath )
{
Pool pool;
svn_client_commit_info_t *commit_info = NULL;
svn_error_t * error =
svn_client_copy( &commit_info,
srcPath.c_str(),
srcRevision.revision(),
destPath.c_str(),
*m_context,
pool );
if( error != NULL )
throw ClientException( error );
}
void Client::Move( const Path & srcPath,
const Revision & srcRevision,
const Path & destPath,
bool force )
{
Pool pool;
svn_client_commit_info_t *commit_info = NULL;
svn_error_t * error =
svn_client_move( &commit_info,
srcPath.c_str(),
srcRevision.revision(),
destPath.c_str(),
force,
*m_context,
pool );
if( error != NULL )
throw ClientException( error );
}
void Client::MkDir( const Path & path,
const std::string& message /*= std::string()*/,
bool make_parents /*= false*/ )
{
Targets targets( path.c_str() );
MkDir( targets, message, make_parents );
}
void Client::MkDir( const Targets & targets,
const std::string& message /*= std::string()*/,
bool make_parents /*= false*/ )
{
Pool pool;
m_context->SetLogMessage( message );
svn_commit_info_t *commit_info = NULL;
svn_error_t * error =
svn_client_mkdir3( &commit_info,
const_cast<apr_array_header_t*>( targets.array( pool ) ),
make_parents,
NULL, /* TODO: Support revprops */
*m_context,
pool );
m_context->ClearLogMessage();
if( error != NULL )
{
throw ClientException( error );
}
}
void Client::Cleanup( const Path & path )
{
Pool subPool;
apr_pool_t * apr_pool = subPool.pool();
svn_error_t * error =
svn_client_cleanup( path.c_str(), *m_context, apr_pool );
if( error != NULL )
throw ClientException( error );
}
void Client::Resolved( const Path & path,
bool recurse )
{
Pool pool;
svn_error_t * error =
svn_client_resolved( path.c_str(),
recurse,
*m_context,
pool );
if( error != NULL )
throw ClientException( error );
}
svn_revnum_t Client::Export( const Path & srcPath,
const Path & destPath,
const Revision & peg,
const Revision & revision,
bool overwrite,
bool ignore_externals,
/*bool ignore_keywords,*/
svn_depth_t depth,
const std::string& native_eol )
{
Pool pool;
svn_revnum_t revnum = 0;
svn_error_t * error = svn_client_export4( &revnum,
srcPath.c_str(),
destPath.c_str(),
const_cast< svn_opt_revision_t* >( peg.revision() ),
const_cast< svn_opt_revision_t* >( revision.revision() ),
overwrite,
ignore_externals,
/*ignore_keywords,*/
depth,
native_eol.empty() ? NULL : native_eol.c_str(),
*m_context,
pool );
if( error != NULL )
{
throw ClientException( error );
}
return revnum;
}
svn_revnum_t Client::Switch( const Path & path,
const char * url,
const Revision & revision,
bool recurse )
{
Pool pool;
svn_revnum_t revnum = 0;
svn_error_t * error =
svn_client_switch( &revnum,
path.c_str(),
url,
revision.revision(),
recurse,
*m_context,
pool );
if( error != NULL )
throw ClientException( error );
return revnum;
}
void Client::Import( const Path & path,
const std::string& url,
const std::string& message,
svn_depth_t depth,
bool no_ignore,
bool ignore_unknown_node_types )
{
Pool pool;
svn_commit_info_t *commit_info = NULL;
m_context->SetLogMessage( message );
svn_error_t * error =
svn_client_import3( &commit_info,
path.c_str(),
url.c_str(),
depth,
no_ignore,
ignore_unknown_node_types,
NULL, /* TODO: Support revprops */
*m_context,
pool );
m_context->ClearLogMessage();
if( error != NULL )
{
throw ClientException( error );
}
}
void Client::Merge( const Path & path1, const Revision & revision1,
const Path & path2, const Revision & revision2,
const Path & localPath, bool force,
bool recurse,
bool notice_ancestry,
bool dry_run )
{
Pool pool;
svn_error_t * error =
svn_client_merge( path1.c_str(),
revision1.revision(),
path2.c_str(),
revision2.revision(),
localPath.c_str(),
recurse,
!notice_ancestry,
force,
dry_run,
*m_context,
pool );
if( error != NULL )
throw ClientException( error );
}
void Client::Relocate( const Path & path,
const char * from_url,
const char * to_url,
bool recurse )
{
Pool pool;
svn_error_t * error =
svn_client_relocate( path.c_str(),
from_url,
to_url,
recurse,
*m_context,
pool );
if( error != NULL )
throw ClientException( error );
}
// A place to collect errors during the lock operation
struct LockNotifyBaton
{
svn_wc_notify_func2_t m_originalNotifyFunction;
void* m_originalNotifyBaton;
NotifyException m_notifyEx;
};
// The notification function used for Lock, collects errors and forwards notifications to the original notification functions
void LockNotifier( void* baton, const svn_wc_notify_t* notify, apr_pool_t* pool )
{
LockNotifyBaton* lockNotifyBaton = static_cast< LockNotifyBaton* >( baton );
if( lockNotifyBaton->m_originalNotifyFunction )
{
(*lockNotifyBaton->m_originalNotifyFunction)( lockNotifyBaton->m_originalNotifyBaton, notify, pool );
}
if ( (svn_wc_notify_failed_lock == notify->action) || (svn_wc_notify_failed_unlock == notify->action) )
{
lockNotifyBaton->m_notifyEx.AddNotification( notify );
}
}
void Client::UpgradeWorkingCopy( const Path & workingCopyPath )
{
Pool pool;
// run the upgrade function
svn_error_t* error = svn_client_upgrade
(
workingCopyPath.c_str(),
*m_context,
pool
);
// check the function's return
if( error != NULL )
{
throw ClientException( error );
}
}
int Client::GetWorkingCopyFormatVersion( const Path& workingCopyPath )
{
Pool pool;
int format = 0;
svn_error_t* error = NULL;
svn_wc_context_t* wcContext = NULL;
error = svn_wc_context_create( &wcContext, NULL, pool, pool );
if( error != NULL )
{
throw ClientException( error );
}
error = svn_wc_check_wc2
(
&format,
wcContext,
workingCopyPath.c_str(),
pool
);
svn_wc_context_destroy( wcContext );
if( error != NULL )
{
throw ClientException( error );
}
return format;
}
void Client::Lock( const Targets & targets,
const std::string& msg /*= std::string()*/,
bool steal_lock /*= false*/ )
{
Pool pool;
// Container to collect errors
LockNotifyBaton lockNotifyBaton;
// save the original notification function
lockNotifyBaton.m_originalNotifyFunction = m_context->ctx()->notify_func2;
lockNotifyBaton.m_originalNotifyBaton = m_context->ctx()->notify_baton2;
// setup the lock notification function
m_context->ctx()->notify_baton2 = &lockNotifyBaton;
m_context->ctx()->notify_func2 = &LockNotifier;
// run the lock function
svn_error_t* error = svn_client_lock
(
const_cast< apr_array_header_t* >( targets.array( pool ) ),
msg.c_str(),
steal_lock,
*m_context,
pool
);
// restore the original notification function
m_context->ctx()->notify_baton2 = lockNotifyBaton.m_originalNotifyBaton;
m_context->ctx()->notify_func2 = lockNotifyBaton.m_originalNotifyFunction;
// check the function's return
if( error != NULL )
{
throw ClientException( error );
}
// check for failed locks
if ( !lockNotifyBaton.m_notifyEx.m_notifications.empty() )
{
throw lockNotifyBaton.m_notifyEx;
}
}
void Client::Unlock( const Targets & targets, bool break_lock /*= false*/ )
{
Pool pool;
// Container to collect errors
LockNotifyBaton lockNotifyBaton;
// save the original notification function
lockNotifyBaton.m_originalNotifyFunction = m_context->ctx()->notify_func2;
lockNotifyBaton.m_originalNotifyBaton = m_context->ctx()->notify_baton2;
// setup the lock notification function
m_context->ctx()->notify_baton2 = &lockNotifyBaton;
m_context->ctx()->notify_func2 = &LockNotifier;
// run the unlock function
svn_error_t * error = svn_client_unlock( const_cast< apr_array_header_t* >( targets.array( pool ) ),
break_lock,
*m_context,
pool );
// restore the original notification function
m_context->ctx()->notify_baton2 = lockNotifyBaton.m_originalNotifyBaton;
m_context->ctx()->notify_func2 = lockNotifyBaton.m_originalNotifyFunction;
if( error != NULL )
{
throw ClientException( error );
}
// check for failed locks
if ( !lockNotifyBaton.m_notifyEx.m_notifications.empty() )
{
throw lockNotifyBaton.m_notifyEx;
}
}
}