Index: subversion/include/svn_client.h =================================================================== --- subversion/include/svn_client.h (revision 14705) +++ subversion/include/svn_client.h (working copy) @@ -1311,18 +1311,35 @@ svn_client_ctx_t *ctx, apr_pool_t *pool); +/** + * @deprecated Provided for backward compatibility with the 1.2 API + * + * Similar to svn_client_copy2(), except that when the @a dst_path is an + * existing directory, a directory with the same name as the basename + * of the @a src_path, is automatically created in the @a dst_path directory. + */ +svn_error_t * +svn_client_copy (svn_client_commit_info_t **commit_info, + const char *src_path, + const svn_opt_revision_t *src_revision, + const char *dst_path, + svn_client_ctx_t *ctx, + apr_pool_t *pool); -/** Copy @a src_path to @a dst_path. +/** + * @since New in 1.3. + * + * Copy @a src_path to @a dst_path. * * @a src_path must be a file or directory under version control, or the - * URL of a versioned item in the repository. If @a src_path is a - * URL, @a src_revision is used to choose the revision from which to copy + * URL of a versioned item in the repository. If @a src_path is a + * URL, @a src_revision is used to choose the revision from which to copy * the @a src_path. @a dst_path must be a file or directory under version * control, or a repository URL, existent or not. * - * If @a dst_path is a URL, use the authentication baton - * in @a ctx and @a ctx->log_msg_func/@a ctx->log_msg_baton to immediately - * attempt to commit the copy action in the repository. If the commit + * If @a dst_path is a URL, use the authentication baton + * in @a ctx and @a ctx->log_msg_func/@a ctx->log_msg_baton to immediately + * attempt to commit the copy action in the repository. If the commit * succeeds, allocate (in @a pool) and populate @a *commit_info. * * If @a dst_path is not a URL, then this is just a @@ -1338,18 +1355,23 @@ * If @a ctx->notify_func2 is non-null, invoke it with @a ctx->notify_baton2 * for each item added at the new location, passing the new, relative path of * the added item. + * + * If @a dir_override is TRUE and @a dst_path is a directory, then a + * directory with the same name as the @a src_path basename will be + * be created in the @a dst_path. If @a dir_override is FALSE then + * an error is returned. */ svn_error_t * -svn_client_copy (svn_client_commit_info_t **commit_info, - const char *src_path, - const svn_opt_revision_t *src_revision, - const char *dst_path, - svn_client_ctx_t *ctx, - apr_pool_t *pool); +svn_client_copy2 (svn_client_commit_info_t **commit_info, + const char *src_path, + const svn_opt_revision_t *src_revision, + const char *dst_path, + svn_client_ctx_t *ctx, + svn_boolean_t dir_override, + apr_pool_t *pool); - /** - * @since New in 1.2. + * @since New in 1.3. * * Move @a src_path to @a dst_path. * @@ -1392,8 +1414,29 @@ * the new location of the thing. * * ### Is this really true? What about svn_wc_notify_commit_replaced()? ### + * + * If @a dir_override is TRUE and @a dst_path is a directory, then a + * directory with the same name as the @a src_path basename will be + * be created in the @a dst_path. If @a dir_override is FALSE then + * an error is returned. */ svn_error_t * +svn_client_move3 (svn_client_commit_info_t **commit_info, + const char *src_path, + const char *dst_path, + svn_boolean_t force, + svn_client_ctx_t *ctx, + svn_boolean_t dir_override, + apr_pool_t *pool); + +/** + * @deprecated Provided for backward compatibility with the 1.2 API + * + * Similar to svn_client_move2(), except that when the @a dst_path is an + * existing directory, a directory with the same name as the basename + * of the @a src_path, is automatically created in the @a dst_path directory. + */ +svn_error_t * svn_client_move2 (svn_client_commit_info_t **commit_info, const char *src_path, const char *dst_path, Index: subversion/libsvn_client/copy.c =================================================================== --- subversion/libsvn_client/copy.c (revision 14705) +++ subversion/libsvn_client/copy.c (working copy) @@ -67,6 +67,7 @@ svn_boolean_t is_move, svn_boolean_t force, svn_client_ctx_t *ctx, + svn_boolean_t dir_override, apr_pool_t *pool) { svn_node_kind_t src_kind, dst_kind; @@ -93,6 +94,12 @@ } else if (dst_kind == svn_node_dir) { + if ((!dir_override) && (src_kind == svn_node_dir)) + { + return svn_error_createf (SVN_ERR_ENTRY_EXISTS, NULL, + _("Directory '%s' already exists"), + svn_path_local_style (dst_path, pool)); + } svn_path_split (src_path, NULL, &base_name, pool); dst_parent = dst_path; } @@ -276,6 +283,7 @@ const svn_opt_revision_t *src_revision, const char *dst_url, svn_client_ctx_t *ctx, + svn_boolean_t dir_override, svn_boolean_t is_move, apr_pool_t *pool) { @@ -403,6 +411,11 @@ /* As a matter of client-side policy, we prevent overwriting any pre-existing directory. So we append src_url's basename to dst_rel, and see if that already exists. */ + if ((!dir_override) && (src_kind == svn_node_dir)) + { + return svn_error_createf (SVN_ERR_FS_ALREADY_EXISTS, NULL, + _("Path '%s' already exists"), dst_rel); + } svn_node_kind_t attempt_kind; const char *bname; @@ -584,6 +597,7 @@ const char *src_path, const char *dst_url, svn_client_ctx_t *ctx, + svn_boolean_t dir_override, apr_pool_t *pool) { const char *anchor, *target, *base_name, *message; @@ -626,6 +640,7 @@ /* BASE_URL defaults to DST_URL. */ base_url = apr_pstrdup (pool, dst_url); + SVN_ERR (svn_io_check_path (base_path, &src_kind, pool)); if (dst_kind == svn_node_none) { /* DST_URL doesn't exist under its parent URL, so the URL we @@ -635,6 +650,12 @@ { /* DST_URL is an existing directory URL. The URL we will be creating, then, is DST_URL+BASENAME. */ + if ((!dir_override) && (src_kind == svn_node_dir)) + { + return svn_error_createf (SVN_ERR_FS_ALREADY_EXISTS, NULL, + _("Directory '%s' already exists"), + dst_url); + } svn_path_split (base_path, NULL, &base_name, pool); base_url = svn_path_url_add_component (base_url, base_name, pool); } @@ -667,7 +688,6 @@ message = ""; /* Crawl the working copy for commit items. */ - SVN_ERR (svn_io_check_path (base_path, &src_kind, pool)); if (src_kind == svn_node_dir) SVN_ERR (svn_wc_adm_retrieve (&dir_access, adm_access, base_path, pool)); else @@ -750,6 +770,7 @@ const svn_opt_revision_t *src_revision, const char *dst_path, svn_client_ctx_t *ctx, + svn_boolean_t dir_override, apr_pool_t *pool) { svn_ra_session_t *ra_session; @@ -820,6 +841,13 @@ if (dst_kind == svn_node_dir) { const char *base_name; + + if ((!dir_override) && (src_kind == svn_node_dir)) + { + return svn_error_createf (SVN_ERR_ENTRY_EXISTS, NULL, + _("Directory '%s' already exists"), + svn_path_local_style (dst_path, pool)); + } svn_path_split (src_url, NULL, &base_name, pool); dst_path = svn_path_join (dst_path, svn_path_uri_decode (base_name, pool), @@ -1012,6 +1040,7 @@ svn_boolean_t is_move, svn_boolean_t force, svn_client_ctx_t *ctx, + svn_boolean_t dir_override, apr_pool_t *pool) { svn_boolean_t src_is_url, dst_is_url; @@ -1090,23 +1119,25 @@ SVN_ERR (wc_to_wc_copy (src_path, dst_path, is_move, force, ctx, + dir_override, pool)); } else if ((! src_is_url) && (dst_is_url)) { SVN_ERR (wc_to_repos_copy (commit_info, src_path, dst_path, - ctx, pool)); + ctx, dir_override, pool)); } else if ((src_is_url) && (! dst_is_url)) { SVN_ERR (repos_to_wc_copy (src_path, src_revision, - dst_path, ctx, + dst_path, ctx, dir_override, pool)); } else { SVN_ERR (repos_to_repos_copy (commit_info, src_path, src_revision, - dst_path, ctx, is_move, pool)); + dst_path, ctx, dir_override, is_move, + pool)); } return SVN_NO_ERROR; @@ -1129,11 +1160,50 @@ FALSE /* is_move */, TRUE /* force, set to avoid deletion check */, ctx, + TRUE, pool); } +svn_error_t * +svn_client_copy2 (svn_client_commit_info_t **commit_info, + const char *src_path, + const svn_opt_revision_t *src_revision, + const char *dst_path, + svn_client_ctx_t *ctx, + svn_boolean_t dir_override, + apr_pool_t *pool) +{ + return setup_copy (commit_info, + src_path, src_revision, dst_path, + FALSE /* is_move */, + TRUE /* force, set to avoid deletion check */, + ctx, + dir_override, + pool); +} svn_error_t * +svn_client_move3 (svn_client_commit_info_t **commit_info, + const char *src_path, + const char *dst_path, + svn_boolean_t force, + svn_client_ctx_t *ctx, + svn_boolean_t dir_override, + apr_pool_t *pool) +{ + const svn_opt_revision_t src_revision + = { svn_opt_revision_unspecified, { 0 } }; + + return setup_copy (commit_info, + src_path, &src_revision, dst_path, + TRUE /* is_move */, + force, + ctx, + dir_override, + pool); +} + +svn_error_t * svn_client_move2 (svn_client_commit_info_t **commit_info, const char *src_path, const char *dst_path, @@ -1149,6 +1219,7 @@ TRUE /* is_move */, force, ctx, + TRUE, pool); } @@ -1181,5 +1252,6 @@ TRUE /* is_move */, force, ctx, + TRUE, pool); }