#include <string.h>
#include "svn_client.h"
#include "svn_error.h"
#include "svn_error_codes.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_opt.h"
#include "svn_time.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_pools.h"
#include "client.h"
#include "mergeinfo.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
#include "private/svn_mergeinfo_private.h"
static svn_error_t *
calculate_target_mergeinfo(svn_ra_session_t *ra_session,
apr_hash_t **target_mergeinfo,
const char *local_abspath,
const char *src_url,
svn_revnum_t src_revnum,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_boolean_t locally_added = FALSE;
apr_hash_t *src_mergeinfo = NULL;
SVN_ERR_ASSERT((local_abspath && !src_url) || (!local_abspath && src_url));
if (local_abspath)
{
const char *repos_root_url;
const char *repos_relpath;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_wc__node_get_origin(NULL, &src_revnum,
&repos_relpath, &repos_root_url,
NULL, NULL,
ctx->wc_ctx, local_abspath, FALSE,
pool, pool));
if (repos_relpath)
{
src_url = svn_path_url_add_component2(repos_root_url, repos_relpath,
pool);
}
else
locally_added = TRUE;
}
if (! locally_added)
{
const char *old_session_url = NULL;
SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url,
ra_session, src_url, pool));
SVN_ERR(svn_client__get_repos_mergeinfo(ra_session, &src_mergeinfo,
"", src_revnum,
svn_mergeinfo_inherited,
TRUE, pool));
if (old_session_url)
SVN_ERR(svn_ra_reparent(ra_session, old_session_url, pool));
}
*target_mergeinfo = src_mergeinfo;
return SVN_NO_ERROR;
}
static svn_error_t *
extend_wc_mergeinfo(const char *target_abspath,
apr_hash_t *mergeinfo,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_hash_t *wc_mergeinfo;
SVN_ERR(svn_client__parse_mergeinfo(&wc_mergeinfo, ctx->wc_ctx,
target_abspath, pool, pool));
if (wc_mergeinfo && mergeinfo)
SVN_ERR(svn_mergeinfo_merge(wc_mergeinfo, mergeinfo, pool));
else if (! wc_mergeinfo)
wc_mergeinfo = mergeinfo;
return svn_error_trace(
svn_client__record_wc_mergeinfo(target_abspath, wc_mergeinfo,
FALSE, ctx, pool));
}
static svn_error_t *
get_copy_pair_ancestors(const apr_array_header_t *copy_pairs,
const char **src_ancestor,
const char **dst_ancestor,
const char **common_ancestor,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
svn_client__copy_pair_t *first;
const char *first_dst;
const char *first_src;
const char *top_dst;
svn_boolean_t src_is_url;
svn_boolean_t dst_is_url;
char *top_src;
int i;
first = APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *);
first_dst = first->dst_abspath_or_url;
dst_is_url = svn_path_is_url(first_dst);
if (copy_pairs->nelts == 1)
top_dst = apr_pstrdup(subpool, first_dst);
else
top_dst = dst_is_url ? svn_uri_dirname(first_dst, subpool)
: svn_dirent_dirname(first_dst, subpool);
first_src = first->src_abspath_or_url;
src_is_url = svn_path_is_url(first_src);
top_src = apr_pstrdup(subpool, first_src);
for (i = 1; i < copy_pairs->nelts; i++)
{
const svn_client__copy_pair_t *pair =
APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *);
top_src = src_is_url
? svn_uri_get_longest_ancestor(top_src, pair->src_abspath_or_url,
subpool)
: svn_dirent_get_longest_ancestor(top_src, pair->src_abspath_or_url,
subpool);
}
if (src_ancestor)
*src_ancestor = apr_pstrdup(pool, top_src);
if (dst_ancestor)
*dst_ancestor = apr_pstrdup(pool, top_dst);
if (common_ancestor)
*common_ancestor =
src_is_url
? svn_uri_get_longest_ancestor(top_src, top_dst, pool)
: svn_dirent_get_longest_ancestor(top_src, top_dst, pool);
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
struct do_wc_to_wc_copies_with_write_lock_baton {
const apr_array_header_t *copy_pairs;
svn_client_ctx_t *ctx;
const char *dst_parent;
};
static svn_error_t *
do_wc_to_wc_copies_with_write_lock(void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct do_wc_to_wc_copies_with_write_lock_baton *b = baton;
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_error_t *err = SVN_NO_ERROR;
for (i = 0; i < b->copy_pairs->nelts; i++)
{
const char *dst_abspath;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(b->copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
if (b->ctx->cancel_func)
SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
dst_abspath = svn_dirent_join(pair->dst_parent_abspath, pair->base_name,
iterpool);
err = svn_wc_copy3(b->ctx->wc_ctx, pair->src_abspath_or_url, dst_abspath,
FALSE ,
b->ctx->cancel_func, b->ctx->cancel_baton,
b->ctx->notify_func2, b->ctx->notify_baton2, iterpool);
if (err)
break;
}
svn_pool_destroy(iterpool);
svn_io_sleep_for_timestamps(b->dst_parent, scratch_pool);
SVN_ERR(err);
return SVN_NO_ERROR;
}
static svn_error_t *
do_wc_to_wc_copies(const apr_array_header_t *copy_pairs,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *dst_parent, *dst_parent_abspath;
struct do_wc_to_wc_copies_with_write_lock_baton baton;
SVN_ERR(get_copy_pair_ancestors(copy_pairs, NULL, &dst_parent, NULL, pool));
if (copy_pairs->nelts == 1)
dst_parent = svn_dirent_dirname(dst_parent, pool);
SVN_ERR(svn_dirent_get_absolute(&dst_parent_abspath, dst_parent, pool));
baton.copy_pairs = copy_pairs;
baton.ctx = ctx;
baton.dst_parent = dst_parent;
SVN_ERR(svn_wc__call_with_write_lock(do_wc_to_wc_copies_with_write_lock,
&baton, ctx->wc_ctx, dst_parent_abspath,
FALSE, pool, pool));
return SVN_NO_ERROR;
}
struct do_wc_to_wc_moves_with_locks_baton {
svn_client_ctx_t *ctx;
svn_client__copy_pair_t *pair;
const char *dst_parent_abspath;
svn_boolean_t lock_src;
svn_boolean_t lock_dst;
};
static svn_error_t *
do_wc_to_wc_moves_with_locks2(void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct do_wc_to_wc_moves_with_locks_baton *b = baton;
const char *dst_abspath;
dst_abspath = svn_dirent_join(b->dst_parent_abspath, b->pair->base_name,
scratch_pool);
SVN_ERR(svn_wc_move(b->ctx->wc_ctx, b->pair->src_abspath_or_url,
dst_abspath, FALSE ,
b->ctx->cancel_func, b->ctx->cancel_baton,
b->ctx->notify_func2, b->ctx->notify_baton2,
scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
do_wc_to_wc_moves_with_locks1(void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct do_wc_to_wc_moves_with_locks_baton *b = baton;
if (b->lock_dst)
SVN_ERR(svn_wc__call_with_write_lock(do_wc_to_wc_moves_with_locks2, b,
b->ctx->wc_ctx, b->dst_parent_abspath,
FALSE, result_pool, scratch_pool));
else
SVN_ERR(do_wc_to_wc_moves_with_locks2(b, result_pool, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
do_wc_to_wc_moves(const apr_array_header_t *copy_pairs,
const char *dst_path,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(pool);
svn_error_t *err = SVN_NO_ERROR;
for (i = 0; i < copy_pairs->nelts; i++)
{
const char *src_parent_abspath;
struct do_wc_to_wc_moves_with_locks_baton baton;
const char *src_wcroot_abspath;
const char *dst_wcroot_abspath;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
src_parent_abspath = svn_dirent_dirname(pair->src_abspath_or_url,
iterpool);
SVN_ERR(svn_wc__get_wc_root(&src_wcroot_abspath,
ctx->wc_ctx, src_parent_abspath,
iterpool, iterpool));
SVN_ERR(svn_wc__get_wc_root(&dst_wcroot_abspath,
ctx->wc_ctx, pair->dst_parent_abspath,
iterpool, iterpool));
if (strcmp(src_parent_abspath, pair->dst_parent_abspath) == 0
|| (svn_dirent_is_child(src_parent_abspath, pair->dst_parent_abspath,
NULL)
&& !svn_dirent_is_child(src_parent_abspath, dst_wcroot_abspath,
NULL)))
{
baton.lock_src = TRUE;
baton.lock_dst = FALSE;
}
else if (svn_dirent_is_child(pair->dst_parent_abspath,
src_parent_abspath, NULL)
&& !svn_dirent_is_child(pair->dst_parent_abspath,
src_wcroot_abspath, NULL))
{
baton.lock_src = FALSE;
baton.lock_dst = TRUE;
}
else
{
baton.lock_src = TRUE;
baton.lock_dst = TRUE;
}
baton.ctx = ctx;
baton.pair = pair;
baton.dst_parent_abspath = pair->dst_parent_abspath;
if (baton.lock_src)
SVN_ERR(svn_wc__call_with_write_lock(do_wc_to_wc_moves_with_locks1,
&baton,
ctx->wc_ctx, src_parent_abspath,
FALSE, iterpool, iterpool));
else
SVN_ERR(do_wc_to_wc_moves_with_locks1(&baton, iterpool, iterpool));
}
svn_pool_destroy(iterpool);
svn_io_sleep_for_timestamps(dst_path, pool);
return svn_error_trace(err);
}
static svn_error_t *
verify_wc_srcs_and_dsts(const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
svn_boolean_t is_move,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_node_kind_t dst_kind, dst_parent_kind;
svn_pool_clear(iterpool);
SVN_ERR(svn_io_check_path(pair->src_abspath_or_url, &pair->src_kind,
iterpool));
if (pair->src_kind == svn_node_none)
return svn_error_createf(
SVN_ERR_NODE_UNKNOWN_KIND, NULL,
_("Path '%s' does not exist"),
svn_dirent_local_style(pair->src_abspath_or_url, pool));
SVN_ERR(svn_io_check_path(pair->dst_abspath_or_url, &dst_kind,
iterpool));
if (dst_kind != svn_node_none)
{
if (is_move
&& copy_pairs->nelts == 1
&& strcmp(svn_dirent_dirname(pair->src_abspath_or_url, iterpool),
svn_dirent_dirname(pair->dst_abspath_or_url,
iterpool)) == 0)
{
const char *dst;
char *dst_apr;
apr_status_t apr_err;
SVN_ERR(svn_path_cstring_from_utf8(&dst,
pair->dst_abspath_or_url,
pool));
apr_err = apr_filepath_merge(&dst_apr, NULL, dst,
APR_FILEPATH_TRUENAME, iterpool);
if (!apr_err)
{
SVN_ERR(svn_path_cstring_to_utf8(&dst, dst_apr, iterpool));
dst = svn_dirent_canonicalize(dst, iterpool);
}
if (!apr_err && strcmp(dst, pair->src_abspath_or_url) == 0)
{
svn_dirent_split(&pair->dst_parent_abspath, &pair->base_name,
pair->dst_abspath_or_url, pool);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
}
return svn_error_createf(
SVN_ERR_ENTRY_EXISTS, NULL,
_("Path '%s' already exists"),
svn_dirent_local_style(pair->dst_abspath_or_url, pool));
}
svn_dirent_split(&pair->dst_parent_abspath, &pair->base_name,
pair->dst_abspath_or_url, pool);
SVN_ERR(svn_io_check_path(pair->dst_parent_abspath, &dst_parent_kind,
iterpool));
if (make_parents && dst_parent_kind == svn_node_none)
{
SVN_ERR(svn_client__make_local_parents(pair->dst_parent_abspath,
TRUE, ctx, iterpool));
}
else if (dst_parent_kind != svn_node_dir)
{
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("Path '%s' is not a directory"),
svn_dirent_local_style(
pair->dst_parent_abspath, pool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
typedef struct path_driver_info_t
{
const char *src_url;
const char *src_path;
const char *dst_path;
svn_node_kind_t src_kind;
svn_revnum_t src_revnum;
svn_boolean_t resurrection;
svn_boolean_t dir_add;
svn_string_t *mergeinfo;
} path_driver_info_t;
struct path_driver_cb_baton
{
const svn_delta_editor_t *editor;
void *edit_baton;
apr_hash_t *action_hash;
svn_boolean_t is_move;
};
static svn_error_t *
path_driver_cb_func(void **dir_baton,
void *parent_baton,
void *callback_baton,
const char *path,
apr_pool_t *pool)
{
struct path_driver_cb_baton *cb_baton = callback_baton;
svn_boolean_t do_delete = FALSE, do_add = FALSE;
path_driver_info_t *path_info = apr_hash_get(cb_baton->action_hash,
path,
APR_HASH_KEY_STRING);
*dir_baton = NULL;
SVN_ERR_ASSERT(! svn_path_is_empty(path));
if (path_info->dir_add)
{
return cb_baton->editor->add_directory(path, parent_baton, NULL,
SVN_INVALID_REVNUM, pool,
dir_baton);
}
if (path_info->resurrection)
{
if (! cb_baton->is_move)
do_add = TRUE;
}
else
{
if (cb_baton->is_move)
{
if (strcmp(path_info->src_path, path) == 0)
do_delete = TRUE;
else
do_add = TRUE;
}
else
{
do_add = TRUE;
}
}
if (do_delete)
{
SVN_ERR(cb_baton->editor->delete_entry(path, SVN_INVALID_REVNUM,
parent_baton, pool));
}
if (do_add)
{
SVN_ERR(svn_path_check_valid(path, pool));
if (path_info->src_kind == svn_node_file)
{
void *file_baton;
SVN_ERR(cb_baton->editor->add_file(path, parent_baton,
path_info->src_url,
path_info->src_revnum,
pool, &file_baton));
if (path_info->mergeinfo)
SVN_ERR(cb_baton->editor->change_file_prop(file_baton,
SVN_PROP_MERGEINFO,
path_info->mergeinfo,
pool));
SVN_ERR(cb_baton->editor->close_file(file_baton, NULL, pool));
}
else
{
SVN_ERR(cb_baton->editor->add_directory(path, parent_baton,
path_info->src_url,
path_info->src_revnum,
pool, dir_baton));
if (path_info->mergeinfo)
SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton,
SVN_PROP_MERGEINFO,
path_info->mergeinfo,
pool));
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
find_absent_parents1(svn_ra_session_t *ra_session,
const char *dir,
apr_array_header_t *new_dirs,
apr_pool_t *pool)
{
svn_node_kind_t kind;
apr_pool_t *iterpool = svn_pool_create(pool);
SVN_ERR(svn_ra_check_path(ra_session, dir, SVN_INVALID_REVNUM, &kind,
iterpool));
while (kind == svn_node_none)
{
svn_pool_clear(iterpool);
APR_ARRAY_PUSH(new_dirs, const char *) = dir;
dir = svn_dirent_dirname(dir, pool);
SVN_ERR(svn_ra_check_path(ra_session, dir, SVN_INVALID_REVNUM,
&kind, iterpool));
}
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists, but is not a "
"directory"), dir);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
find_absent_parents2(svn_ra_session_t *ra_session,
const char **top_dst_url,
apr_array_header_t *new_dirs,
apr_pool_t *pool)
{
const char *root_url = *top_dst_url;
svn_node_kind_t kind;
SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
pool));
while (kind == svn_node_none)
{
APR_ARRAY_PUSH(new_dirs, const char *) = root_url;
root_url = svn_uri_dirname(root_url, pool);
SVN_ERR(svn_ra_reparent(ra_session, root_url, pool));
SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
pool));
}
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists, but is not a directory"),
root_url);
*top_dst_url = root_url;
return SVN_NO_ERROR;
}
static svn_error_t *
repos_to_repos_copy(const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
svn_boolean_t is_move,
apr_pool_t *pool)
{
svn_error_t *err;
apr_array_header_t *paths = apr_array_make(pool, 2 * copy_pairs->nelts,
sizeof(const char *));
apr_hash_t *action_hash = apr_hash_make(pool);
apr_array_header_t *path_infos;
const char *top_url, *top_url_all, *top_url_dst;
const char *message, *repos_root, *ignored_url;
svn_revnum_t youngest = SVN_INVALID_REVNUM;
svn_ra_session_t *ra_session = NULL;
const svn_delta_editor_t *editor;
void *edit_baton;
struct path_driver_cb_baton cb_baton;
apr_array_header_t *new_dirs = NULL;
apr_hash_t *commit_revprops;
int i;
svn_client__copy_pair_t *first_pair =
APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *);
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL,
first_pair->src_abspath_or_url,
NULL, NULL, FALSE, TRUE,
ctx, pool));
SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, pool));
path_infos = apr_array_make(pool, copy_pairs->nelts,
sizeof(path_driver_info_t *));
for (i = 0; i < copy_pairs->nelts; i++)
{
path_driver_info_t *info = apr_pcalloc(pool, sizeof(*info));
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
apr_hash_t *mergeinfo;
svn_opt_revision_t *src_rev, *ignored_rev, dead_end_rev;
dead_end_rev.kind = svn_opt_revision_unspecified;
if (! (svn_uri__is_ancestor(repos_root, pair->src_abspath_or_url)
&& svn_uri__is_ancestor(repos_root, pair->dst_abspath_or_url)))
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Source and destination URLs appear not to point to the "
"same repository."));
SVN_ERR(svn_client__get_revision_number(&pair->src_revnum, &youngest,
ctx->wc_ctx, NULL,
ra_session,
&pair->src_op_revision, pool));
SVN_ERR(svn_client__ensure_ra_session_url(&ignored_url, ra_session,
pair->src_abspath_or_url,
pool));
SVN_ERR(svn_client__repos_locations(&pair->src_abspath_or_url, &src_rev,
&ignored_url, &ignored_rev,
ra_session,
pair->src_abspath_or_url,
&pair->src_peg_revision,
&pair->src_op_revision,
&dead_end_rev, ctx, pool));
SVN_ERR(svn_client__ensure_ra_session_url(&ignored_url, ra_session,
pair->src_abspath_or_url,
pool));
SVN_ERR(calculate_target_mergeinfo(ra_session, &mergeinfo, NULL,
pair->src_abspath_or_url,
pair->src_revnum, ctx, pool));
if (mergeinfo)
SVN_ERR(svn_mergeinfo_to_string(&info->mergeinfo, mergeinfo, pool));
info->src_url = pair->src_abspath_or_url;
info->src_revnum = pair->src_revnum;
info->resurrection = FALSE;
APR_ARRAY_PUSH(path_infos, path_driver_info_t *) = info;
}
SVN_ERR(get_copy_pair_ancestors(copy_pairs, NULL, &top_url_dst, &top_url_all,
pool));
top_url = is_move ? top_url_all : top_url_dst;
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
path_driver_info_t *info = APR_ARRAY_IDX(path_infos, i,
path_driver_info_t *);
if (strcmp(pair->src_abspath_or_url, pair->dst_abspath_or_url) == 0)
info->resurrection = TRUE;
if ((strcmp(top_url, pair->dst_abspath_or_url) == 0)
&& (strcmp(top_url, repos_root) != 0))
{
top_url = svn_uri_dirname(top_url, pool);
}
if (is_move
&& (strcmp(top_url, pair->src_abspath_or_url) == 0)
&& (strcmp(top_url, repos_root) != 0))
{
top_url = svn_uri_dirname(top_url, pool);
}
}
SVN_ERR(svn_client__ensure_ra_session_url(&ignored_url, ra_session,
top_url, pool));
if (make_parents)
{
const char *dir;
new_dirs = apr_array_make(pool, 0, sizeof(const char *));
if (is_move)
{
dir = svn_uri__is_child(
top_url,
svn_uri_dirname(first_pair->dst_abspath_or_url, pool),
pool);
if (dir)
SVN_ERR(find_absent_parents1(ra_session, dir, new_dirs, pool));
}
else
{
apr_array_header_t *new_urls =
apr_array_make(pool, 0, sizeof(const char *));
SVN_ERR(find_absent_parents2(ra_session, &top_url, new_urls, pool));
for (i = 0; i < new_urls->nelts; i++)
{
const char *new_url = APR_ARRAY_IDX(new_urls, i, const char *);
dir = svn_uri__is_child(top_url, new_url, pool);
APR_ARRAY_PUSH(new_dirs, const char *) = dir ? dir : "";
}
}
}
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
path_driver_info_t *info = APR_ARRAY_IDX(path_infos, i,
path_driver_info_t *);
if ((strcmp(pair->dst_abspath_or_url, repos_root) != 0)
&& (svn_uri__is_child(pair->dst_abspath_or_url,
pair->src_abspath_or_url, pool) != NULL))
{
info->resurrection = TRUE;
top_url = svn_uri_dirname(top_url, pool);
SVN_ERR(svn_ra_reparent(ra_session, top_url, pool));
}
}
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair =
APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *);
path_driver_info_t *info =
APR_ARRAY_IDX(path_infos, i, path_driver_info_t *);
svn_node_kind_t dst_kind;
const char *src_rel, *dst_rel;
src_rel = svn_uri__is_child(top_url, pair->src_abspath_or_url, pool);
if (src_rel)
{
SVN_ERR(svn_ra_check_path(ra_session, src_rel, pair->src_revnum,
&info->src_kind, pool));
}
else if (strcmp(pair->src_abspath_or_url, top_url) == 0)
{
src_rel = "";
SVN_ERR(svn_ra_check_path(ra_session, src_rel, pair->src_revnum,
&info->src_kind, pool));
}
else
{
const char *old_url = NULL;
src_rel = NULL;
SVN_ERR_ASSERT(! is_move);
SVN_ERR(svn_client__ensure_ra_session_url(&old_url, ra_session,
pair->src_abspath_or_url,
pool));
SVN_ERR(svn_ra_check_path(ra_session, "", pair->src_revnum,
&info->src_kind, pool));
SVN_ERR(svn_ra_reparent(ra_session, old_url, pool));
}
if (info->src_kind == svn_node_none)
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' does not exist in revision %ld"),
pair->src_abspath_or_url, pair->src_revnum);
dst_rel = svn_uri__is_child(top_url, pair->dst_abspath_or_url, pool);
if (! dst_rel)
dst_rel = "";
SVN_ERR(svn_ra_check_path(ra_session, dst_rel, youngest,
&dst_kind, pool));
if (dst_kind != svn_node_none)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists"), dst_rel);
info->src_path = src_rel;
info->dst_path = dst_rel;
apr_hash_set(action_hash, info->dst_path, APR_HASH_KEY_STRING, info);
if (is_move && (! info->resurrection))
apr_hash_set(action_hash, info->src_path, APR_HASH_KEY_STRING, info);
}
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, 2 * copy_pairs->nelts, sizeof(item));
if (make_parents)
{
for (i = 0; i < new_dirs->nelts; i++)
{
const char *relpath = APR_ARRAY_IDX(new_dirs, i, const char *);
item = svn_client_commit_item3_create(pool);
item->url = svn_path_url_add_component2(top_url, relpath, pool);
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
}
for (i = 0; i < path_infos->nelts; i++)
{
path_driver_info_t *info = APR_ARRAY_IDX(path_infos, i,
path_driver_info_t *);
item = svn_client_commit_item3_create(pool);
item->url = svn_path_url_add_component2(top_url, info->dst_path, pool);
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
if (is_move && (! info->resurrection))
{
item = apr_pcalloc(pool, sizeof(*item));
item->url = svn_path_url_add_component2(top_url, info->src_path, 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(&message, &tmp_file, commit_items,
ctx, pool));
if (! message)
return SVN_NO_ERROR;
}
else
message = "";
if (make_parents)
{
for (i = 0; i < new_dirs->nelts; i++)
{
const char *relpath = APR_ARRAY_IDX(new_dirs, i, const char *);
path_driver_info_t *info = apr_pcalloc(pool, sizeof(*info));
info->dst_path = relpath;
info->dir_add = TRUE;
APR_ARRAY_PUSH(paths, const char *) = relpath;
apr_hash_set(action_hash, relpath, APR_HASH_KEY_STRING, info);
}
}
for (i = 0; i < path_infos->nelts; i++)
{
path_driver_info_t *info = APR_ARRAY_IDX(path_infos, i,
path_driver_info_t *);
APR_ARRAY_PUSH(paths, const char *) = info->dst_path;
if (is_move && (! info->resurrection))
APR_ARRAY_PUSH(paths, const char *) = info->src_path;
}
SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
message, ctx, pool));
SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
commit_revprops,
commit_callback,
commit_baton,
NULL, TRUE,
pool));
cb_baton.editor = editor;
cb_baton.edit_baton = edit_baton;
cb_baton.action_hash = action_hash;
cb_baton.is_move = is_move;
err = svn_delta_path_driver(editor, edit_baton, youngest, paths,
path_driver_cb_func, &cb_baton, pool);
if (err)
{
svn_error_clear(editor->abort_edit(edit_baton, pool));
return svn_error_trace(err);
}
return svn_error_trace(editor->close_edit(edit_baton, pool));
}
struct check_url_kind_baton
{
svn_ra_session_t *session;
const char *repos_root_url;
svn_boolean_t should_reparent;
};
static svn_error_t *
check_url_kind(void *baton,
svn_node_kind_t *kind,
const char *url,
svn_revnum_t revision,
apr_pool_t *scratch_pool)
{
struct check_url_kind_baton *cukb = baton;
if (!svn_uri__is_ancestor(cukb->repos_root_url, url))
*kind = svn_node_none;
else
{
cukb->should_reparent = TRUE;
SVN_ERR(svn_ra_reparent(cukb->session, url, scratch_pool));
SVN_ERR(svn_ra_check_path(cukb->session, "", revision,
kind, scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
wc_to_repos_copy(const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *message;
const char *top_src_path, *top_dst_url;
struct check_url_kind_baton cukb;
const char *top_src_abspath;
svn_ra_session_t *ra_session;
const svn_delta_editor_t *editor;
void *edit_baton;
svn_client__committables_t *committables;
apr_array_header_t *commit_items;
apr_pool_t *iterpool;
apr_array_header_t *new_dirs = NULL;
apr_hash_t *commit_revprops;
svn_client__copy_pair_t *first_pair;
int i;
SVN_ERR(get_copy_pair_ancestors(copy_pairs, &top_src_path, NULL, NULL, pool));
iterpool = svn_pool_create(pool);
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
SVN_ERR(svn_wc__node_get_base_rev(&pair->src_revnum, ctx->wc_ctx,
pair->src_abspath_or_url, iterpool));
}
first_pair = APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *);
top_dst_url = svn_uri_dirname(first_pair->dst_abspath_or_url, pool);
for (i = 1; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
top_dst_url = svn_uri_get_longest_ancestor(top_dst_url,
pair->dst_abspath_or_url,
pool);
}
SVN_ERR(svn_dirent_get_absolute(&top_src_abspath, top_src_path, pool));
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, top_dst_url,
top_src_abspath, NULL, TRUE,
TRUE, ctx, pool));
if (make_parents)
{
new_dirs = apr_array_make(pool, 0, sizeof(const char *));
SVN_ERR(find_absent_parents2(ra_session, &top_dst_url, new_dirs, pool));
}
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_node_kind_t dst_kind;
const char *dst_rel;
svn_client__copy_pair_t *pair =
APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
dst_rel = svn_uri__is_child(top_dst_url,
pair->dst_abspath_or_url,
iterpool);
SVN_ERR(svn_ra_check_path(ra_session, dst_rel, SVN_INVALID_REVNUM,
&dst_kind, iterpool));
if (dst_kind != svn_node_none)
{
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists"),
pair->dst_abspath_or_url);
}
}
if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
{
svn_client_commit_item3_t *item;
const char *tmp_file;
commit_items = apr_array_make(pool, copy_pairs->nelts, sizeof(item));
if (make_parents)
{
for (i = 0; i < new_dirs->nelts; i++)
{
const char *url = APR_ARRAY_IDX(new_dirs, i, const char *);
item = svn_client_commit_item3_create(pool);
item->url = url;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
}
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
item = svn_client_commit_item3_create(pool);
item->url = pair->dst_abspath_or_url;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
ctx, pool));
if (! message)
{
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
}
else
message = "";
SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
message, ctx, pool));
cukb.session = ra_session;
SVN_ERR(svn_ra_get_repos_root2(ra_session, &cukb.repos_root_url, pool));
cukb.should_reparent = FALSE;
SVN_ERR(svn_client__get_copy_committables(&committables,
copy_pairs,
check_url_kind, &cukb,
ctx, pool, pool));
commit_items = apr_hash_get(committables->by_repository,
cukb.repos_root_url,
APR_HASH_KEY_STRING);
SVN_ERR_ASSERT(commit_items != NULL);
if (cukb.should_reparent)
SVN_ERR(svn_ra_reparent(ra_session, top_dst_url, pool));
if (make_parents)
{
for (i = 0; i < new_dirs->nelts; i++)
{
const char *url = APR_ARRAY_IDX(new_dirs, i, const char *);
svn_client_commit_item3_t *item;
item = svn_client_commit_item3_create(pool);
item->url = url;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
item->incoming_prop_changes = apr_array_make(pool, 1,
sizeof(svn_prop_t *));
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
}
for (i = 0; i < copy_pairs->nelts; i++)
{
apr_hash_t *mergeinfo, *wc_mergeinfo;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_client_commit_item3_t *item =
APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *);
svn_pool_clear(iterpool);
item->outgoing_prop_changes = apr_array_make(pool, 1,
sizeof(svn_prop_t *));
SVN_ERR(calculate_target_mergeinfo(ra_session, &mergeinfo,
pair->src_abspath_or_url,
NULL, SVN_INVALID_REVNUM,
ctx, iterpool));
SVN_ERR(svn_client__parse_mergeinfo(&wc_mergeinfo, ctx->wc_ctx,
pair->src_abspath_or_url,
iterpool, iterpool));
if (wc_mergeinfo && mergeinfo)
SVN_ERR(svn_mergeinfo_merge(mergeinfo, wc_mergeinfo, iterpool));
else if (! mergeinfo)
mergeinfo = wc_mergeinfo;
if (mergeinfo)
{
svn_prop_t *mergeinfo_prop
= apr_palloc(item->outgoing_prop_changes->pool,
sizeof(svn_prop_t));
svn_string_t *prop_value;
SVN_ERR(svn_mergeinfo_to_string(&prop_value, mergeinfo,
item->outgoing_prop_changes->pool));
mergeinfo_prop->name = SVN_PROP_MERGEINFO;
mergeinfo_prop->value = prop_value;
APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *)
= mergeinfo_prop;
}
}
SVN_ERR(svn_client__condense_commit_items(&top_dst_url,
commit_items, pool));
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, top_dst_url,
NULL, commit_items,
FALSE, FALSE, ctx, pool));
SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
commit_revprops,
commit_callback,
commit_baton, NULL,
TRUE,
pool));
SVN_ERR_W(svn_client__do_commit(top_dst_url, commit_items,
editor, edit_baton,
0,
NULL, NULL, ctx, pool, pool),
_("Commit failed (details follow):"));
svn_io_sleep_for_timestamps(top_src_path, pool);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
struct notification_adjust_baton
{
svn_wc_notify_func2_t inner_func;
void *inner_baton;
const char *checkout_abspath;
const char *final_abspath;
};
static void
notification_adjust_func(void *baton,
const svn_wc_notify_t *notify,
apr_pool_t *pool)
{
struct notification_adjust_baton *nb = baton;
svn_wc_notify_t *inner_notify = svn_wc_dup_notify(notify, pool);
const char *relpath;
relpath = svn_dirent_skip_ancestor(nb->checkout_abspath, notify->path);
inner_notify->path = svn_dirent_join(nb->final_abspath, relpath, pool);
if (nb->inner_func)
nb->inner_func(nb->inner_baton, inner_notify, pool);
}
static svn_error_t *
repos_to_wc_copy_single(svn_client__copy_pair_t *pair,
svn_boolean_t same_repositories,
svn_boolean_t ignore_externals,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_hash_t *src_mergeinfo;
const char *dst_abspath = pair->dst_abspath_or_url;
SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
if (pair->src_kind == svn_node_dir)
{
svn_boolean_t sleep_needed = FALSE;
const char *tmp_abspath;
SVN_ERR(svn_wc_create_tmp_file2(NULL, &tmp_abspath, dst_abspath,
svn_io_file_del_on_close, pool));
{
svn_wc_notify_func2_t old_notify_func2 = ctx->notify_func2;
void *old_notify_baton2 = ctx->notify_baton2;
struct notification_adjust_baton nb;
svn_error_t *err;
nb.inner_func = ctx->notify_func2;
nb.inner_baton = ctx->notify_baton2;
nb.checkout_abspath = tmp_abspath;
nb.final_abspath = dst_abspath;
ctx->notify_func2 = notification_adjust_func;
ctx->notify_baton2 = &nb;
err = svn_client__checkout_internal(&pair->src_revnum,
pair->src_original,
tmp_abspath,
&pair->src_peg_revision,
&pair->src_op_revision,
svn_depth_infinity,
ignore_externals, FALSE,
&sleep_needed, ctx, pool);
ctx->notify_func2 = old_notify_func2;
ctx->notify_baton2 = old_notify_baton2;
SVN_ERR(err);
}
if (same_repositories)
{
SVN_ERR(svn_wc_copy3(ctx->wc_ctx, tmp_abspath, dst_abspath,
TRUE ,
ctx->cancel_func, ctx->cancel_baton,
NULL, NULL, pool));
SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx, tmp_abspath,
FALSE, pool, pool));
SVN_ERR(svn_wc_remove_from_revision_control2(ctx->wc_ctx,
tmp_abspath,
FALSE, FALSE,
ctx->cancel_func,
ctx->cancel_baton,
pool));
SVN_ERR(svn_io_file_rename(tmp_abspath, dst_abspath, pool));
}
else
{
SVN_ERR(svn_wc__rename_wc(ctx->wc_ctx, tmp_abspath, dst_abspath,
pool));
svn_io_sleep_for_timestamps(dst_abspath, pool);
return svn_error_createf(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Source URL '%s' is from foreign repository; "
"leaving it as a disjoint WC"), pair->src_abspath_or_url);
}
}
else if (pair->src_kind == svn_node_file)
{
svn_stream_t *fstream;
const char *new_text_path;
apr_hash_t *new_props;
const char *src_rel;
svn_stream_t *new_base_contents;
SVN_ERR(svn_stream_open_unique(&fstream, &new_text_path, NULL,
svn_io_file_del_on_pool_cleanup, pool,
pool));
SVN_ERR(svn_ra_get_path_relative_to_session(ra_session, &src_rel,
pair->src_abspath_or_url,
pool));
SVN_ERR(svn_ra_get_file(ra_session, src_rel, pair->src_revnum, fstream,
&pair->src_revnum, &new_props, pool));
SVN_ERR(svn_stream_close(fstream));
SVN_ERR(svn_stream_open_readonly(&new_base_contents, new_text_path,
pool, pool));
SVN_ERR(svn_wc_add_repos_file4(
ctx->wc_ctx, dst_abspath,
new_base_contents, NULL, new_props, NULL,
same_repositories ? pair->src_abspath_or_url : NULL,
same_repositories ? pair->src_revnum : SVN_INVALID_REVNUM,
ctx->cancel_func, ctx->cancel_baton,
pool));
}
SVN_ERR(calculate_target_mergeinfo(ra_session, &src_mergeinfo, NULL,
pair->src_abspath_or_url,
pair->src_revnum, ctx, pool));
SVN_ERR(extend_wc_mergeinfo(dst_abspath, src_mergeinfo, ctx, pool));
if (ctx->notify_func2)
{
svn_wc_notify_t *notify = svn_wc_create_notify(
dst_abspath, svn_wc_notify_add, pool);
notify->kind = pair->src_kind;
(*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
}
svn_io_sleep_for_timestamps(dst_abspath, pool);
return SVN_NO_ERROR;
}
static svn_error_t*
repos_to_wc_copy_locked(const apr_array_header_t *copy_pairs,
const char *top_dst_path,
svn_boolean_t ignore_externals,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
int i;
const char *src_uuid = NULL, *dst_uuid = NULL;
svn_boolean_t same_repositories;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_node_kind_t kind;
svn_boolean_t is_excluded;
svn_boolean_t is_server_excluded;
svn_pool_clear(iterpool);
SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, pair->dst_abspath_or_url,
FALSE, iterpool));
if (kind == svn_node_none)
continue;
SVN_ERR(svn_wc__node_is_status_excluded(&is_excluded, ctx->wc_ctx,
pair->dst_abspath_or_url,
iterpool));
if (is_excluded)
{
return svn_error_createf
(SVN_ERR_ENTRY_EXISTS,
NULL, _("'%s' is already under version control"),
svn_dirent_local_style(pair->dst_abspath_or_url, iterpool));
}
SVN_ERR(svn_wc__node_is_status_server_excluded(&is_server_excluded,
ctx->wc_ctx,
pair->dst_abspath_or_url,
iterpool));
if (is_server_excluded)
{
return svn_error_createf
(SVN_ERR_ENTRY_EXISTS,
NULL, _("'%s' is already under version control"),
svn_dirent_local_style(pair->dst_abspath_or_url, iterpool));
}
if (kind != svn_node_dir)
{
svn_boolean_t is_deleted;
svn_boolean_t is_not_present;
SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, ctx->wc_ctx,
pair->dst_abspath_or_url,
iterpool));
SVN_ERR(svn_wc__node_is_status_not_present(&is_not_present,
ctx->wc_ctx,
pair->dst_abspath_or_url,
iterpool));
if ((! is_deleted) && (! is_not_present))
return svn_error_createf
(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Entry for '%s' exists (though the working file is missing)"),
svn_dirent_local_style(pair->dst_abspath_or_url, iterpool));
}
}
{
svn_error_t *src_err, *dst_err;
const char *parent;
const char *parent_abspath;
src_err = svn_ra_get_uuid2(ra_session, &src_uuid, scratch_pool);
if (src_err && src_err->apr_err != SVN_ERR_RA_NO_REPOS_UUID)
return svn_error_trace(src_err);
if (copy_pairs->nelts == 1)
parent = svn_dirent_dirname(top_dst_path, scratch_pool);
else
parent = top_dst_path;
SVN_ERR(svn_dirent_get_absolute(&parent_abspath, parent, scratch_pool));
dst_err = svn_client_uuid_from_path2(&dst_uuid, parent_abspath, ctx,
scratch_pool, scratch_pool);
if (dst_err && dst_err->apr_err != SVN_ERR_RA_NO_REPOS_UUID)
return dst_err;
if (src_err || dst_err || (! src_uuid) || (! dst_uuid))
same_repositories = FALSE;
else
same_repositories = (strcmp(src_uuid, dst_uuid) == 0);
}
for (i = 0; i < copy_pairs->nelts; i++)
{
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
svn_pool_clear(iterpool);
SVN_ERR(repos_to_wc_copy_single(APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *),
same_repositories,
ignore_externals,
ra_session, ctx, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
struct repos_to_wc_copy_baton {
const apr_array_header_t *copy_pairs;
const char *top_dst_path;
svn_boolean_t ignore_externals;
svn_ra_session_t *ra_session;
svn_client_ctx_t *ctx;
};
static svn_error_t *
repos_to_wc_copy_cb(void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct repos_to_wc_copy_baton *b = baton;
SVN_ERR(repos_to_wc_copy_locked(b->copy_pairs, b->top_dst_path,
b->ignore_externals, b->ra_session,
b->ctx, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
repos_to_wc_copy(const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
svn_boolean_t ignore_externals,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_ra_session_t *ra_session;
const char *top_src_url, *top_dst_path;
apr_pool_t *iterpool = svn_pool_create(pool);
const char *lock_abspath;
struct repos_to_wc_copy_baton baton;
int i;
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
const char *src, *ignored_url;
svn_opt_revision_t *new_rev, *ignored_rev, dead_end_rev;
svn_pool_clear(iterpool);
dead_end_rev.kind = svn_opt_revision_unspecified;
SVN_ERR(svn_client__repos_locations(&src, &new_rev,
&ignored_url, &ignored_rev,
NULL,
pair->src_abspath_or_url,
&pair->src_peg_revision,
&pair->src_op_revision,
&dead_end_rev,
ctx, iterpool));
pair->src_original = pair->src_abspath_or_url;
pair->src_abspath_or_url = apr_pstrdup(pool, src);
}
SVN_ERR(get_copy_pair_ancestors(copy_pairs, &top_src_url, &top_dst_path,
NULL, pool));
lock_abspath = top_dst_path;
if (copy_pairs->nelts == 1)
{
top_src_url = svn_uri_dirname(top_src_url, pool);
lock_abspath = svn_dirent_dirname(top_dst_path, pool);
}
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, top_src_url,
NULL, NULL, FALSE, TRUE,
ctx, pool));
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
SVN_ERR(svn_client__get_revision_number(&pair->src_revnum, NULL,
ctx->wc_ctx, NULL, ra_session,
&pair->src_op_revision, pool));
}
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_node_kind_t dst_parent_kind, dst_kind;
const char *dst_parent;
const char *src_rel;
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_get_path_relative_to_session(ra_session, &src_rel,
pair->src_abspath_or_url,
iterpool));
SVN_ERR(svn_ra_check_path(ra_session, src_rel, pair->src_revnum,
&pair->src_kind, pool));
if (pair->src_kind == svn_node_none)
{
if (SVN_IS_VALID_REVNUM(pair->src_revnum))
return svn_error_createf
(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' not found in revision %ld"),
pair->src_abspath_or_url, pair->src_revnum);
else
return svn_error_createf
(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' not found in head revision"),
pair->src_abspath_or_url);
}
SVN_ERR(svn_io_check_path(pair->dst_abspath_or_url, &dst_kind,
iterpool));
if (dst_kind != svn_node_none)
{
return svn_error_createf(
SVN_ERR_ENTRY_EXISTS, NULL,
_("Path '%s' already exists"),
svn_dirent_local_style(pair->dst_abspath_or_url, pool));
}
dst_parent = svn_dirent_dirname(pair->dst_abspath_or_url, iterpool);
SVN_ERR(svn_io_check_path(dst_parent, &dst_parent_kind, iterpool));
if (make_parents && dst_parent_kind == svn_node_none)
{
SVN_ERR(svn_client__make_local_parents(dst_parent, TRUE, ctx,
iterpool));
}
else if (dst_parent_kind != svn_node_dir)
{
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("Path '%s' is not a directory"),
svn_dirent_local_style(dst_parent, pool));
}
}
svn_pool_destroy(iterpool);
baton.copy_pairs = copy_pairs;
baton.top_dst_path = top_dst_path;
baton.ignore_externals = ignore_externals;
baton.ra_session = ra_session;
baton.ctx = ctx;
SVN_ERR(svn_wc__call_with_write_lock(repos_to_wc_copy_cb, &baton,
ctx->wc_ctx, lock_abspath,
FALSE, pool, pool));
return SVN_NO_ERROR;
}
#define NEED_REPOS_REVNUM(revision) \
((revision.kind != svn_opt_revision_unspecified) \
&& (revision.kind != svn_opt_revision_working))
static svn_error_t *
try_copy(const apr_array_header_t *sources,
const char *dst_path_in,
svn_boolean_t is_move,
svn_boolean_t make_parents,
svn_boolean_t ignore_externals,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_array_header_t *copy_pairs =
apr_array_make(pool, sources->nelts,
sizeof(svn_client__copy_pair_t *));
svn_boolean_t srcs_are_urls, dst_is_url;
int i;
srcs_are_urls = svn_path_is_url(APR_ARRAY_IDX(sources, 0,
svn_client_copy_source_t *)->path);
dst_is_url = svn_path_is_url(dst_path_in);
if (!dst_is_url)
SVN_ERR(svn_dirent_get_absolute(&dst_path_in, dst_path_in, pool));
if (sources->nelts > 1)
{
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < sources->nelts; i++)
{
svn_client_copy_source_t *source = APR_ARRAY_IDX(sources, i,
svn_client_copy_source_t *);
svn_client__copy_pair_t *pair = apr_palloc(pool, sizeof(*pair));
const char *src_basename;
svn_boolean_t src_is_url = svn_path_is_url(source->path);
svn_pool_clear(iterpool);
if (src_is_url)
{
pair->src_abspath_or_url = apr_pstrdup(pool, source->path);
src_basename = svn_uri_basename(pair->src_abspath_or_url,
iterpool);
}
else
{
SVN_ERR(svn_dirent_get_absolute(&pair->src_abspath_or_url,
source->path, pool));
src_basename = svn_dirent_basename(pair->src_abspath_or_url,
iterpool);
}
pair->src_op_revision = *source->revision;
pair->src_peg_revision = *source->peg_revision;
SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision,
&pair->src_op_revision,
src_is_url,
TRUE,
iterpool));
if (src_is_url != srcs_are_urls)
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot mix repository and working copy sources"));
if (dst_is_url)
pair->dst_abspath_or_url =
svn_path_url_add_component2(dst_path_in, src_basename, pool);
else
pair->dst_abspath_or_url = svn_dirent_join(dst_path_in,
src_basename, pool);
APR_ARRAY_PUSH(copy_pairs, svn_client__copy_pair_t *) = pair;
}
svn_pool_destroy(iterpool);
}
else
{
svn_client__copy_pair_t *pair = apr_palloc(pool, sizeof(*pair));
svn_client_copy_source_t *source =
APR_ARRAY_IDX(sources, 0, svn_client_copy_source_t *);
svn_boolean_t src_is_url = svn_path_is_url(source->path);
if (src_is_url)
pair->src_abspath_or_url = apr_pstrdup(pool, source->path);
else
SVN_ERR(svn_dirent_get_absolute(&pair->src_abspath_or_url,
source->path, pool));
pair->src_op_revision = *source->revision;
pair->src_peg_revision = *source->peg_revision;
SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision,
&pair->src_op_revision,
src_is_url, TRUE, pool));
pair->dst_abspath_or_url = dst_path_in;
APR_ARRAY_PUSH(copy_pairs, svn_client__copy_pair_t *) = pair;
}
if (!srcs_are_urls && !dst_is_url)
{
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
if (svn_dirent_is_child(pair->src_abspath_or_url,
pair->dst_abspath_or_url, iterpool))
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot copy path '%s' into its own child '%s'"),
svn_dirent_local_style(pair->src_abspath_or_url, pool),
svn_dirent_local_style(pair->dst_abspath_or_url, pool));
}
svn_pool_destroy(iterpool);
}
if (is_move && !srcs_are_urls)
{
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair =
APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *);
svn_node_kind_t external_kind;
const char *defining_abspath;
svn_pool_clear(iterpool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(pair->src_abspath_or_url));
SVN_ERR(svn_wc__read_external_info(&external_kind, &defining_abspath,
NULL, NULL, NULL, ctx->wc_ctx,
pair->src_abspath_or_url,
pair->src_abspath_or_url, TRUE,
iterpool, iterpool));
if (external_kind != svn_node_none)
return svn_error_createf(
SVN_ERR_WC_CANNOT_MOVE_FILE_EXTERNAL,
NULL,
_("Cannot move the external at '%s'; please "
"edit the svn:externals property on '%s'."),
svn_dirent_local_style(pair->src_abspath_or_url, pool),
svn_dirent_local_style(defining_abspath, pool));
}
svn_pool_destroy(iterpool);
}
if (is_move)
{
if (srcs_are_urls != dst_is_url)
{
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Moves between the working copy and the repository are not "
"supported"));
}
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
if (strcmp(pair->src_abspath_or_url,
pair->dst_abspath_or_url) == 0)
return svn_error_createf(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
srcs_are_urls ?
_("Cannot move URL '%s' into itself") :
_("Cannot move path '%s' into itself"),
srcs_are_urls ?
pair->src_abspath_or_url :
svn_dirent_local_style(pair->src_abspath_or_url, pool));
}
}
else
{
if (!srcs_are_urls)
{
svn_boolean_t need_repos_op_rev = FALSE;
svn_boolean_t need_repos_peg_rev = FALSE;
for (i = 0; i < copy_pairs->nelts; i++)
{
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
if (NEED_REPOS_REVNUM(pair->src_op_revision))
need_repos_op_rev = TRUE;
if (NEED_REPOS_REVNUM(pair->src_peg_revision))
need_repos_peg_rev = TRUE;
if (need_repos_op_rev || need_repos_peg_rev)
break;
}
if (need_repos_op_rev || need_repos_peg_rev)
{
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < copy_pairs->nelts; i++)
{
const char *copyfrom_repos_root_url;
const char *copyfrom_repos_relpath;
const char *url;
svn_revnum_t copyfrom_rev;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
svn_pool_clear(iterpool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(pair->src_abspath_or_url));
SVN_ERR(svn_wc__node_get_origin(NULL, ©from_rev,
©from_repos_relpath,
©from_repos_root_url,
NULL, NULL,
ctx->wc_ctx,
pair->src_abspath_or_url,
TRUE, iterpool, iterpool));
if (copyfrom_repos_relpath)
url = svn_path_url_add_component2(copyfrom_repos_root_url,
copyfrom_repos_relpath,
pool);
else
return svn_error_createf
(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' does not have a URL associated with it"),
svn_dirent_local_style(pair->src_abspath_or_url, pool));
pair->src_abspath_or_url = url;
if (!need_repos_peg_rev
|| pair->src_peg_revision.kind == svn_opt_revision_base)
{
pair->src_peg_revision.kind = svn_opt_revision_number;
pair->src_peg_revision.value.number = copyfrom_rev;
}
if (pair->src_op_revision.kind == svn_opt_revision_base)
{
pair->src_op_revision.kind = svn_opt_revision_number;
pair->src_op_revision.value.number = copyfrom_rev;
}
}
svn_pool_destroy(iterpool);
srcs_are_urls = TRUE;
}
}
}
if ((! srcs_are_urls) && (! dst_is_url))
{
SVN_ERR(verify_wc_srcs_and_dsts(copy_pairs, make_parents, is_move,
ctx, pool));
if (is_move)
return svn_error_trace(do_wc_to_wc_moves(copy_pairs, dst_path_in, ctx,
pool));
else
return svn_error_trace(do_wc_to_wc_copies(copy_pairs, ctx, pool));
}
else if ((! srcs_are_urls) && (dst_is_url))
{
return svn_error_trace(
wc_to_repos_copy(copy_pairs, make_parents, revprop_table,
commit_callback, commit_baton, ctx, pool));
}
else if ((srcs_are_urls) && (! dst_is_url))
{
return svn_error_trace(
repos_to_wc_copy(copy_pairs, make_parents, ignore_externals,
ctx, pool));
}
else
{
return svn_error_trace(
repos_to_repos_copy(copy_pairs, make_parents, revprop_table,
commit_callback, commit_baton, ctx, is_move,
pool));
}
}
svn_error_t *
svn_client_copy6(const apr_array_header_t *sources,
const char *dst_path,
svn_boolean_t copy_as_child,
svn_boolean_t make_parents,
svn_boolean_t ignore_externals,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_error_t *err;
apr_pool_t *subpool = svn_pool_create(pool);
if (sources->nelts > 1 && !copy_as_child)
return svn_error_create(SVN_ERR_CLIENT_MULTIPLE_SOURCES_DISALLOWED,
NULL, NULL);
err = try_copy(sources, dst_path,
FALSE ,
make_parents,
ignore_externals,
revprop_table,
commit_callback, commit_baton,
ctx,
subpool);
if (copy_as_child && err && (sources->nelts == 1)
&& (err->apr_err == SVN_ERR_ENTRY_EXISTS
|| err->apr_err == SVN_ERR_FS_ALREADY_EXISTS))
{
const char *src_path = APR_ARRAY_IDX(sources, 0,
svn_client_copy_source_t *)->path;
const char *src_basename;
svn_boolean_t src_is_url = svn_path_is_url(src_path);
svn_boolean_t dst_is_url = svn_path_is_url(dst_path);
svn_error_clear(err);
svn_pool_clear(subpool);
src_basename = src_is_url ? svn_uri_basename(src_path, subpool)
: svn_dirent_basename(src_path, subpool);
err = try_copy(sources,
dst_is_url
? svn_path_url_add_component2(dst_path, src_basename,
subpool)
: svn_dirent_join(dst_path, src_basename, subpool),
FALSE ,
make_parents,
ignore_externals,
revprop_table,
commit_callback, commit_baton,
ctx,
subpool);
}
svn_pool_destroy(subpool);
return svn_error_trace(err);
}
svn_error_t *
svn_client_move6(const apr_array_header_t *src_paths,
const char *dst_path,
svn_boolean_t move_as_child,
svn_boolean_t make_parents,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const svn_opt_revision_t head_revision
= { svn_opt_revision_head, { 0 } };
svn_error_t *err;
int i;
apr_pool_t *subpool = svn_pool_create(pool);
apr_array_header_t *sources = apr_array_make(pool, src_paths->nelts,
sizeof(const svn_client_copy_source_t *));
if (src_paths->nelts > 1 && !move_as_child)
return svn_error_create(SVN_ERR_CLIENT_MULTIPLE_SOURCES_DISALLOWED,
NULL, NULL);
for (i = 0; i < src_paths->nelts; i++)
{
const char *src_path = APR_ARRAY_IDX(src_paths, i, const char *);
svn_client_copy_source_t *copy_source = apr_palloc(pool,
sizeof(*copy_source));
copy_source->path = src_path;
copy_source->revision = &head_revision;
copy_source->peg_revision = &head_revision;
APR_ARRAY_PUSH(sources, svn_client_copy_source_t *) = copy_source;
}
err = try_copy(sources, dst_path,
TRUE ,
make_parents,
FALSE,
revprop_table,
commit_callback, commit_baton,
ctx,
subpool);
if (move_as_child && err && (src_paths->nelts == 1)
&& (err->apr_err == SVN_ERR_ENTRY_EXISTS
|| err->apr_err == SVN_ERR_FS_ALREADY_EXISTS))
{
const char *src_path = APR_ARRAY_IDX(src_paths, 0, const char *);
const char *src_basename;
svn_boolean_t src_is_url = svn_path_is_url(src_path);
svn_boolean_t dst_is_url = svn_path_is_url(dst_path);
svn_error_clear(err);
svn_pool_clear(subpool);
src_basename = src_is_url ? svn_uri_basename(src_path, pool)
: svn_dirent_basename(src_path, pool);
err = try_copy(sources,
dst_is_url
? svn_path_url_add_component2(dst_path,
src_basename, pool)
: svn_dirent_join(dst_path, src_basename, pool),
TRUE ,
make_parents,
FALSE,
revprop_table,
commit_callback, commit_baton,
ctx,
subpool);
}
svn_pool_destroy(subpool);
return svn_error_trace(err);
}