Index: subversion/tests/cmdline/basic_tests.py =================================================================== --- subversion/tests/cmdline/basic_tests.py (revision 967311) +++ subversion/tests/cmdline/basic_tests.py (working copy) @@ -2565,7 +2565,7 @@ test_list = [ None, delete_keep_local_twice, windows_paths_in_repos, basic_rm_urls_one_repo, - XFail(basic_rm_urls_multi_repos), + basic_rm_urls_multi_repos, automatic_conflict_resolution, info_nonexisting_file, basic_relative_url_using_current_dir, Index: subversion/libsvn_client/delete.c =================================================================== --- subversion/libsvn_client/delete.c (revision 967311) +++ subversion/libsvn_client/delete.c (working copy) @@ -134,59 +134,42 @@ path_driver_cb_func(void **dir_baton, return editor->delete_entry(path, SVN_INVALID_REVNUM, parent_baton, pool); } - static svn_error_t * -delete_urls(const apr_array_header_t *paths, - const apr_hash_t *revprop_table, - svn_client_ctx_t *ctx, - apr_pool_t *pool) +single_repos_delete(svn_ra_session_t *ra_session, + const char *repos_root, + const apr_array_header_t *relpaths, + const apr_hash_t *revprop_table, + svn_client_ctx_t *ctx, + apr_pool_t *pool) { - svn_ra_session_t *ra_session = NULL; const svn_delta_editor_t *editor; + apr_hash_t *commit_revprops; void *edit_baton; const char *log_msg; - svn_node_kind_t kind; - apr_array_header_t *targets; - apr_hash_t *commit_revprops; + int i; svn_error_t *err; - const char *common; - int i; - apr_pool_t *subpool = svn_pool_create(pool); - /* Condense our list of deletion targets. */ - SVN_ERR(svn_uri_condense_targets(&common, &targets, paths, TRUE, - pool, pool)); - if (! targets->nelts) - { - const char *bname; - svn_uri_split(&common, &bname, common, pool); - APR_ARRAY_PUSH(targets, const char *) = svn_path_uri_decode(bname, pool); - } - /* Create new commit items and add them to the array. */ if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) { svn_client_commit_item3_t *item; const char *tmp_file; apr_array_header_t *commit_items - = apr_array_make(pool, targets->nelts, sizeof(item)); + = apr_array_make(pool, relpaths->nelts, sizeof(item)); - for (i = 0; i < targets->nelts; i++) + for (i = 0; i < relpaths->nelts; i++) { - const char *path = APR_ARRAY_IDX(targets, i, const char *); + const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *); item = svn_client_commit_item3_create(pool); - item->url = svn_uri_join(common, path, pool); + item->url = svn_uri_join(repos_root, relpath, pool); item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE; APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; } SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items, ctx, pool)); if (! log_msg) - { - svn_pool_destroy(subpool); - return SVN_NO_ERROR; - } + return SVN_NO_ERROR; } else log_msg = ""; @@ -194,46 +177,9 @@ static svn_error_t * SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, log_msg, ctx, pool)); - /* Verify that each thing to be deleted actually exists (to prevent - the creation of a revision that has no changes, since the - filesystem allows for no-op deletes). While here, we'll - URI-decode our targets. */ - for (i = 0; i < targets->nelts; i++) - { - const char *path = APR_ARRAY_IDX(targets, i, const char *); - const char *item_url; + /* Reparent the RA_session to the repos_root. */ + SVN_ERR(svn_ra_reparent(ra_session, repos_root, pool)); - svn_pool_clear(subpool); - item_url = svn_path_url_add_component2(common, path, subpool); - path = svn_path_uri_decode(path, pool); - APR_ARRAY_IDX(targets, i, const char *) = path; - - /* If we've not yet done so, open an RA session for the - URL. Note that we don't have a local directory, nor a place - to put temp files. Otherwise, reparent our existing - session. */ - if (! ra_session) - { - SVN_ERR(svn_client__open_ra_session_internal(&ra_session, item_url, - NULL, NULL, FALSE, - TRUE, ctx, pool)); - } - else - { - SVN_ERR(svn_ra_reparent(ra_session, item_url, subpool)); - } - - SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, - &kind, subpool)); - if (kind == svn_node_none) - return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, - "URL '%s' does not exist", item_url); - } - svn_pool_destroy(subpool); - - /* Reparent the RA_session to the common parent of our deletees. */ - SVN_ERR(svn_ra_reparent(ra_session, common, pool)); - /* Fetch RA commit editor */ SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, commit_revprops, @@ -244,7 +190,7 @@ static svn_error_t * /* Call the path-based editor driver. */ err = svn_delta_path_driver(editor, edit_baton, SVN_INVALID_REVNUM, - targets, path_driver_cb_func, + relpaths, path_driver_cb_func, (void *)editor, pool); if (err) { @@ -257,6 +203,97 @@ static svn_error_t * return editor->close_edit(edit_baton, pool); } +static svn_error_t * +delete_urls_multi_repos(const apr_array_header_t *uris, + const apr_hash_t *revprop_table, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + apr_hash_t *sessions = apr_hash_make(pool); + apr_hash_t *relpaths = apr_hash_make(pool); + apr_hash_index_t *hi; + int i; + + /* Create a hash of repos_root -> ra_session maps and repos_root -> relpaths + maps, used to group the various targets. */ + for (i = 0; i < uris->nelts; i++) + { + const char *uri = APR_ARRAY_IDX(uris, i, const char *); + svn_ra_session_t *ra_session = NULL; + const char *repos_root = NULL; + const char *repos_relpath = NULL; + apr_array_header_t *relpaths_list; + svn_node_kind_t kind; + + for (hi = apr_hash_first(pool, sessions); hi; hi = apr_hash_next(hi)) + { + repos_root = svn__apr_hash_index_key(hi); + repos_relpath = svn_uri_is_child(repos_root, uri, pool); + + if (repos_relpath) + { + /* Great! We've found another uri underneath this session, + store it and move on. */ + ra_session = svn__apr_hash_index_val(hi); + relpaths_list = apr_hash_get(relpaths, repos_root, + APR_HASH_KEY_STRING); + + APR_ARRAY_PUSH(relpaths_list, const char *) = + svn_path_uri_decode(repos_relpath, pool); + + break; + } + } + + if (!ra_session) + { + /* If we haven't found a session yet, we need to open one up. + Note that we don't have a local directory, nor a place + to put temp files. */ + SVN_ERR(svn_client__open_ra_session_internal(&ra_session, uri, + NULL, NULL, FALSE, + TRUE, ctx, pool)); + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, pool)); + + apr_hash_set(sessions, repos_root, APR_HASH_KEY_STRING, ra_session); + repos_relpath = svn_uri_is_child(repos_root, uri, pool); + + relpaths_list = apr_array_make(pool, 1, sizeof(const char *)); + apr_hash_set(relpaths, repos_root, APR_HASH_KEY_STRING, + relpaths_list); + APR_ARRAY_PUSH(relpaths_list, const char *) = + svn_path_uri_decode(repos_relpath, pool); + } + + /* Now, test to see if the thing actually exists. */ + SVN_ERR(svn_ra_reparent(ra_session, uri, pool)); + SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind, + pool)); + if (kind == svn_node_none) + return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, + "URL '%s' does not exist", uri); + } + + /* At this point, we should have two hashs: + SESSIONS maps repos_roots to ra_sessions. + RELPATHS maps repos_roots to a list of decoded relpaths for that root. + + Now we iterate over the collection of sessions and do a commit for each + one with the collected relpaths. */ + for (hi = apr_hash_first(pool, sessions); hi; hi = apr_hash_next(hi)) + { + const char *repos_root = svn__apr_hash_index_key(hi); + svn_ra_session_t *ra_session = svn__apr_hash_index_val(hi); + apr_array_header_t *relpaths_list = apr_hash_get(relpaths, repos_root, + APR_HASH_KEY_STRING); + + SVN_ERR(single_repos_delete(ra_session, repos_root, relpaths_list, + revprop_table, ctx, pool)); + } + + return SVN_NO_ERROR; +} + svn_error_t * svn_client__wc_delete(const char *path, svn_boolean_t force, @@ -323,7 +360,7 @@ svn_client_delete4(const apr_array_header_t *paths if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *))) { - SVN_ERR(delete_urls(paths, revprop_table, ctx, pool)); + SVN_ERR(delete_urls_multi_repos(paths, revprop_table, ctx, pool)); } else {