Index: subversion/include/private/svn_wc_private.h =================================================================== --- subversion/include/private/svn_wc_private.h (revision 1292379) +++ subversion/include/private/svn_wc_private.h (working copy) @@ -263,6 +263,15 @@ apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Close the DBs for any externals under LOCAL_ABSPATH. Detection of + externals is as per svn_wc__externals_gather_definitions. + + Perform temporary allocations in SCRATCH_POOL. */ +svn_error_t * +svn_wc__externals_close(const char *local_abspath, + svn_wc_context_t *wc_ctx, + apr_pool_t *scratch_pool); + /** Set @a *tree_conflict to a newly allocated @c * svn_wc_conflict_description_t structure describing the tree * conflict state of @a victim_abspath, or to @c NULL if @a victim_abspath Index: subversion/libsvn_client/copy.c =================================================================== --- subversion/libsvn_client/copy.c (revision 1292379) +++ subversion/libsvn_client/copy.c (working copy) @@ -1519,6 +1519,17 @@ ctx->notify_baton2 = old_notify_baton2; SVN_ERR(err); + +#ifdef WIN32 + if (!ignore_externals) + { + /* Issue #4123: We may still hold file handles to the databases + for externals under TMP_ABSPATH. We need to release these + handles before we move TMP_ABSPATH below or Windows will + raise an ERROR_ACCESS_DENIED error. */ + SVN_ERR(svn_wc__externals_close(tmp_abspath, ctx->wc_ctx, pool)); + } +#endif } /* Schedule dst_path for addition in parent, with copy history. Index: subversion/libsvn_wc/externals.c =================================================================== --- subversion/libsvn_wc/externals.c (revision 1292379) +++ subversion/libsvn_wc/externals.c (working copy) @@ -1363,6 +1363,56 @@ } } +svn_error_t * +svn_wc__externals_close(const char *local_abspath, + svn_wc_context_t *wc_ctx, + apr_pool_t *scratch_pool) +{ + apr_hash_t *externals; + apr_hash_index_t *hi; + apr_pool_t *iterpool; + + SVN_ERR(svn_wc__externals_gather_definitions(&externals, NULL, wc_ctx, + local_abspath, + svn_depth_infinity, + scratch_pool, scratch_pool)); + + if (!apr_hash_count(externals)) + return SVN_NO_ERROR; + + iterpool = svn_pool_create(scratch_pool); + + for (hi = apr_hash_first(scratch_pool, externals); + hi; + hi = apr_hash_next(hi)) + { + const char *this_abspath = svn__apr_hash_index_key(hi); + const char *external_value = svn__apr_hash_index_val(hi); + apr_array_header_t *ext_desc; + int i; + + SVN_ERR(svn_wc_parse_externals_description3(&ext_desc, this_abspath, + external_value, FALSE, + iterpool)); + for (i = 0; i < ext_desc->nelts; i++) + { + svn_wc_external_item2_t *ext_item = + APR_ARRAY_IDX(ext_desc, i, svn_wc_external_item2_t *); + const char *external_abspath; + + SVN_ERR(svn_dirent_get_absolute(&external_abspath, + svn_dirent_join(this_abspath, + ext_item->target_dir, + iterpool), + iterpool)); + SVN_ERR(svn_wc__db_drop_root(wc_ctx->db, external_abspath, + iterpool)); + } + } + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + /* Return the scheme of @a uri in @a scheme allocated from @a pool. If @a uri does not appear to be a valid URI, then @a scheme will not be updated. */ Index: subversion/tests/cmdline/externals_tests.py =================================================================== --- subversion/tests/cmdline/externals_tests.py (revision 1292379) +++ subversion/tests/cmdline/externals_tests.py (working copy) @@ -2787,7 +2787,6 @@ # Test for issue #4123 'URL-to-WC copy of externals fails on Windows' @Issue(4123) -@XFail(svntest.main.is_os_windows) def url_to_wc_copy_of_externals(sbox): "url-to-wc copy of externals" @@ -2805,7 +2804,8 @@ svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) # Copy ^/A/C to External-WC-to-URL-Copy. - # Currently this fails with: + # + # Previously this failed with: # >svn copy ^^/A/C External-WC-to-URL-Copy # U External-WC-to-URL-Copy #