static gpointer copy_thread (gpointer user_data)
{
  struct thread_args *args = user_data;
  svn_error_t *err;
  svn_opt_revision_t revision;
  apr_array_header_t *paths;
  svn_client_copy_source_t copy_source;
  svn_client_ctx_t *ctx = args->ctx;
  apr_pool_t *subpool, *pool = args->pool;
  TshNotifyDialog *dialog = args->dialog;
  gchar *from = args->from;
  gchar *to = args->to;
  gchar *error_str;
#if CHECK_SVN_VERSION_S(1,6)
  svn_commit_info_t *commit_info;
  gchar *message;
  gchar buffer[256];
#endif

  g_free (args);

  subpool = svn_pool_create (pool);

  paths = apr_array_make (subpool, 1, sizeof (svn_client_copy_source_t *));

  revision.kind = svn_opt_revision_unspecified;
  copy_source.path = from;
  copy_source.revision = &revision;
  copy_source.peg_revision = &revision;
  APR_ARRAY_PUSH (paths, svn_client_copy_source_t *) = &copy_source;

#if CHECK_SVN_VERSION_G(1,9)
  if ((err = svn_client_copy7(paths, to, FALSE, FALSE, FALSE, FALSE, FALSE,
                              NULL, NULL, tsh_commit_func2, dialog, ctx,
                              subpool)))
#elif CHECK_SVN_VERSION_G(1,7)
  if ((err = svn_client_copy6(paths, to, FALSE, FALSE, FALSE, NULL,
                              tsh_commit_func2, dialog, ctx, subpool)))
#elif CHECK_SVN_VERSION_G(1,6)
  if ((err = svn_client_copy5(&commit_info, paths, to, FALSE, FALSE, FALSE,
                              NULL, ctx, subpool)))
#else
  if ((err = svn_client_copy4(&commit_info, paths, to, FALSE, FALSE, NULL,
                              ctx, subpool)))
#endif
  {
    svn_pool_destroy (subpool);

    error_str = tsh_strerror(err);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    gdk_threads_enter();
    tsh_notify_dialog_add(dialog, _("Failed"), error_str, NULL);
    tsh_notify_dialog_done (dialog);
    gdk_threads_leave();
G_GNUC_END_IGNORE_DEPRECATIONS

    g_free(error_str);

    svn_error_clear(err);
    tsh_reset_cancel();
    return GINT_TO_POINTER (FALSE);
  }

#if CHECK_SVN_VERSION_S(1,6)
  if(SVN_IS_VALID_REVNUM(commit_info->revision))
  {
    g_snprintf(buffer, 256, _("At revision: %ld"), commit_info->revision);
    message = buffer;
  }
  else
  {
    message = _("Local copy");
  }
#endif

  svn_pool_destroy (subpool);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  gdk_threads_enter();
#if CHECK_SVN_VERSION_S(1,6)
  tsh_notify_dialog_add(dialog, _("Completed"), message, NULL);
#endif
  tsh_notify_dialog_done (dialog);
  gdk_threads_leave();
G_GNUC_END_IGNORE_DEPRECATIONS

  tsh_reset_cancel();
  return GINT_TO_POINTER (TRUE);
}
Py::Object pysvn_client::cmd_copy2( const Py::Tuple &a_args, const Py::Dict &a_kws )
{
    static argument_description args_desc[] =
    {
    { true, name_sources },
    { true, name_dest_url_or_path },
    { false, name_copy_as_child },
    { false, name_make_parents },
    { false, name_revprops },
    { false, NULL }
    };
    FunctionArguments args( "copy2", args_desc, a_args, a_kws );
    args.check();

    SvnPool pool( m_context );
    pysvn_commit_info_t *commit_info = NULL;

    std::string type_error_message;
    try
    {
        type_error_message = "expecting list for sources (arg 1)";
        Py::List list_all_sources = args.getArg( name_sources );

        apr_array_header_t *all_sources =
            apr_array_make( pool, list_all_sources.length(), sizeof(svn_client_copy_source_t *) );

        for( unsigned int index=0; index<list_all_sources.length(); index++ )
        {
            Py::Tuple tuple_src_rev_pegrev( list_all_sources[ index ] );

            std::string src_url_or_path;
            svn_opt_revision_t *revision = reinterpret_cast<svn_opt_revision_t *>( apr_palloc( pool, sizeof( svn_opt_revision_t ) ) );
            svn_opt_revision_t *peg_revision = reinterpret_cast<svn_opt_revision_t *>( apr_palloc( pool, sizeof( svn_opt_revision_t ) ) );

            if( tuple_src_rev_pegrev.length() > 3 )
            {
                std::string msg = "copy2() expecting tuple with 2 or 3 values in sources list";
                throw Py::AttributeError( msg );
            }

            type_error_message = "expecting string for 1st tuple value in sources list";
            Py::String py_src_url_or_path( tuple_src_rev_pegrev[0] );
            if( py_src_url_or_path.isUnicode() )
            {
                Py::String utf8( py_src_url_or_path.encode( name_utf8 ) );
                src_url_or_path = py_src_url_or_path.as_std_string();
            }
            else
            {
                src_url_or_path = py_src_url_or_path.as_std_string();
            }
            std::string norm_src_url_or_path( svnNormalisedIfPath( src_url_or_path, pool ) );
            bool is_url = is_svn_url( norm_src_url_or_path );

            if( tuple_src_rev_pegrev.length() >= 2 )
            {
                Py::Object obj( tuple_src_rev_pegrev[1] );

                if( pysvn_revision::check( obj ) )
                {
                    pysvn_revision *rev = static_cast<pysvn_revision *>( obj.ptr() );
                    *revision = rev->getSvnRevision();

                    revisionKindCompatibleCheck( is_url, *revision,
                        "sources list 2nd tuple value", "sources list 1st tuple value" );
                }
                else
                {
                    std::string msg = "copy2() expecting revision for 2nd tuple value in sources list";
                    throw Py::AttributeError( msg );
                }
            }
            else
            {
                if( is_url )
                {
                    revision->kind = svn_opt_revision_head;
                }
                else
                {
                    revision->kind = svn_opt_revision_working;
                }
            }

            if( tuple_src_rev_pegrev.length() >= 3 )
            {
                Py::Object obj( tuple_src_rev_pegrev[2] );

                if( pysvn_revision::check( obj ) )
                {
                    pysvn_revision *rev = static_cast<pysvn_revision *>( obj.ptr() );
                    *peg_revision = rev->getSvnRevision();

                    revisionKindCompatibleCheck( is_url, *peg_revision,
                        "sources list 2nd tuple value", "sources list 1st tuple value" );
                }
                else
                {
                    std::string msg = "copy2() expecting revision for 3rd tuple value in sources list";
                    throw Py::AttributeError( msg );
                }
            }
            else
            {
                *peg_revision = *revision;
            }

            svn_client_copy_source_t *source = reinterpret_cast<svn_client_copy_source_t *>( apr_palloc( pool, sizeof(*source) ) );
            source->path = apr_pstrdup( pool, norm_src_url_or_path.c_str() );
            source->revision = revision;
            source->peg_revision = peg_revision;

            APR_ARRAY_PUSH( all_sources, svn_client_copy_source_t *) = source;
        }

        type_error_message = "expecting string for dest_url_or_path";
        Py::String dest_path( args.getUtf8String( name_dest_url_or_path ) );

        type_error_message = "expecting boolean for keyword copy_as_child";
        bool copy_as_child = args.getBoolean( name_copy_as_child, false );

        type_error_message = "expecting boolean for keyword make_parents";
        bool make_parents = args.getBoolean( name_make_parents, false );

        apr_hash_t *revprops = NULL;
        if( args.hasArg( name_revprops ) )
        {
            Py::Object py_revprop = args.getArg( name_revprops );
            if( !py_revprop.isNone() )
            {
                revprops = hashOfStringsFromDistOfStrings( py_revprop, pool );
            }
        }

        try
        {
            std::string norm_dest_path( svnNormalisedIfPath( dest_path, pool ) );

            checkThreadPermission();

            PythonAllowThreads permission( m_context );

            // behavior changed
            svn_error_t *error = svn_client_copy4
                (
                &commit_info,
                all_sources,
                norm_dest_path.c_str(),
                copy_as_child,
                make_parents,
                revprops,
                m_context,
                pool
                );
            permission.allowThisThread();
            if( error != NULL )
            {
                throw SvnException( error );
            }
        }
        catch( SvnException &e )
        {
            // use callback error over ClientException
            m_context.checkForError( m_module.client_error );

            throw_client_error( e );
        }
    }
    catch( Py::TypeError & )
    {
        throw Py::TypeError( type_error_message );
    }

    return toObject( commit_info );
}