#include <string.h>
#include <assert.h>
#include "svn_wc.h"
#include "svn_client.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_opt.h"
#include "svn_time.h"
#include "client.h"
#include "svn_private_config.h"
static svn_error_t *
wc_to_wc_copy(const char *src_path,
const char *dst_path,
svn_boolean_t is_move,
svn_boolean_t force,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_node_kind_t src_kind, dst_kind, dst_parent_kind;
const char *dst_parent, *base_name;
svn_wc_adm_access_t *adm_access, *src_access;
svn_error_t *err;
SVN_ERR(svn_io_check_path(src_path, &src_kind, pool));
if (src_kind == svn_node_none)
return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
_("Path '%s' does not exist"),
svn_path_local_style(src_path, pool));
SVN_ERR(svn_io_check_path(dst_path, &dst_kind, pool));
if (dst_kind != svn_node_none)
return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
_("Path '%s' already exists"),
svn_path_local_style(dst_path, pool));
svn_path_split(dst_path, &dst_parent, &base_name, pool);
SVN_ERR(svn_io_check_path(dst_parent, &dst_parent_kind, pool));
if (dst_parent_kind != svn_node_dir)
return svn_error_createf(SVN_ERR_WC_NOT_DIRECTORY, NULL,
_("Path '%s' is not a directory"),
svn_path_local_style(dst_parent, pool));
if (is_move)
{
const char *src_parent;
svn_path_split(src_path, &src_parent, NULL, pool);
SVN_ERR(svn_wc_adm_open3(&src_access, NULL, src_parent, TRUE,
src_kind == svn_node_dir ? -1 : 0,
ctx->cancel_func, ctx->cancel_baton, pool));
if (strcmp(src_parent, dst_parent) == 0)
{
adm_access = src_access;
}
else
{
const char *src_parent_abs, *dst_parent_abs;
SVN_ERR(svn_path_get_absolute(&src_parent_abs, src_parent, pool));
SVN_ERR(svn_path_get_absolute(&dst_parent_abs, dst_parent, pool));
if ((src_kind == svn_node_dir)
&& (svn_path_is_child(src_parent_abs, dst_parent_abs, pool)))
{
SVN_ERR(svn_wc_adm_retrieve(&adm_access, src_access,
dst_parent, pool));
}
else
{
SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, dst_parent,
TRUE, 0, ctx->cancel_func,
ctx->cancel_baton,pool));
}
}
if (!force)
SVN_ERR_W(svn_client__can_delete(src_path, ctx, pool),
_("Move will not be attempted unless forced"));
}
else
{
SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, dst_parent, TRUE, 0,
ctx->cancel_func, ctx->cancel_baton, pool));
}
err = svn_wc_copy2(src_path, adm_access, base_name,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2, pool);
svn_sleep_for_timestamps();
SVN_ERR(err);
if (is_move)
{
SVN_ERR(svn_wc_delete2(src_path, src_access,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2, pool));
if (adm_access != src_access)
SVN_ERR(svn_wc_adm_close(adm_access));
SVN_ERR(svn_wc_adm_close(src_access));
}
else
{
SVN_ERR(svn_wc_adm_close(adm_access));
}
return SVN_NO_ERROR;
}
struct path_driver_cb_baton
{
const svn_delta_editor_t *editor;
void *edit_baton;
svn_node_kind_t src_kind;
const char *src_url;
const char *src_path;
const char *dst_path;
svn_boolean_t is_move;
svn_boolean_t resurrection;
svn_revnum_t src_revnum;
};
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;
*dir_baton = NULL;
assert(! svn_path_is_empty(path));
if (cb_baton->resurrection)
{
if (! cb_baton->is_move)
do_add = TRUE;
}
else
{
if (cb_baton->is_move)
{
if (strcmp(cb_baton->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 (cb_baton->src_kind == svn_node_file)
{
void *file_baton;
SVN_ERR(cb_baton->editor->add_file(path, parent_baton,
cb_baton->src_url,
cb_baton->src_revnum,
pool, &file_baton));
SVN_ERR(cb_baton->editor->close_file(file_baton, NULL, pool));
}
else
{
SVN_ERR(cb_baton->editor->add_directory(path, parent_baton,
cb_baton->src_url,
cb_baton->src_revnum,
pool, dir_baton));
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
repos_to_repos_copy(svn_commit_info_t **commit_info_p,
const char *src_url,
const svn_opt_revision_t *src_revision,
const char *dst_url,
svn_client_ctx_t *ctx,
svn_boolean_t is_move,
apr_pool_t *pool)
{
apr_array_header_t *paths = apr_array_make(pool, 2, sizeof(const char *));
const char *top_url, *src_rel, *dst_rel, *message, *repos_root;
svn_revnum_t youngest;
svn_ra_session_t *ra_session;
svn_node_kind_t src_kind, dst_kind;
const svn_delta_editor_t *editor;
void *edit_baton;
void *commit_baton;
svn_revnum_t src_revnum;
svn_boolean_t resurrection = FALSE;
struct path_driver_cb_baton cb_baton;
svn_error_t *err;
top_url = svn_path_get_longest_ancestor(src_url, dst_url, pool);
if (strcmp(src_url, dst_url) == 0)
{
resurrection = TRUE;
top_url = svn_path_dirname(top_url, pool);
}
err = svn_client__open_ra_session_internal(&ra_session, top_url,
NULL, NULL, NULL, FALSE, TRUE,
ctx, pool);
if (err)
{
if ((err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
&& ((top_url == NULL) || (top_url[0] == '\0')))
{
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Source and dest appear not to be in the same repository "
"(src: '%s'; dst: '%s')"),
src_url, dst_url);
}
else
return err;
}
SVN_ERR(svn_ra_get_repos_root(ra_session, &repos_root, pool));
if (strcmp(dst_url, repos_root) != 0
&& svn_path_is_child(dst_url, src_url, pool) != NULL)
{
resurrection = TRUE;
top_url = svn_path_dirname(top_url, pool);
SVN_ERR(svn_ra_reparent(ra_session, top_url, pool));
}
src_rel = svn_path_is_child(top_url, src_url, pool);
if (src_rel)
src_rel = svn_path_uri_decode(src_rel, pool);
else
src_rel = "";
dst_rel = svn_path_is_child(top_url, dst_url, pool);
if (dst_rel)
dst_rel = svn_path_uri_decode(dst_rel, pool);
else
dst_rel = "";
if (svn_path_is_empty(src_rel) && is_move)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot move URL '%s' into itself"), src_url);
SVN_ERR(svn_client__get_revision_number
(&src_revnum, ra_session, src_revision, NULL, pool));
SVN_ERR(svn_ra_get_latest_revnum(ra_session, &youngest, pool));
if (! SVN_IS_VALID_REVNUM(src_revnum))
src_revnum = youngest;
SVN_ERR(svn_ra_check_path(ra_session, src_rel, src_revnum, &src_kind,
pool));
if (src_kind == svn_node_none)
return svn_error_createf
(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' does not exist in revision %ld"),
src_url, src_revnum);
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);
}
if (ctx->log_msg_func || ctx->log_msg_func2)
{
svn_client_commit_item2_t *item;
const char *tmp_file;
apr_array_header_t *commit_items
= apr_array_make(pool, 2, sizeof(item));
item = apr_pcalloc(pool, sizeof(*item));
item->url = svn_path_join(top_url, dst_rel, pool);
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item2_t *) = item;
if (is_move && (! resurrection))
{
item = apr_pcalloc(pool, sizeof(*item));
item->url = svn_path_join(top_url, src_rel, pool);
item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item2_t *) = item;
}
SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
ctx, pool));
if (! message)
return SVN_NO_ERROR;
}
else
message = "";
SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
SVN_ERR(svn_ra_get_commit_editor2(ra_session, &editor, &edit_baton,
message,
svn_client__commit_callback,
commit_baton,
NULL, TRUE,
pool));
APR_ARRAY_PUSH(paths, const char *) = dst_rel;
if (is_move && (! resurrection))
APR_ARRAY_PUSH(paths, const char *) = src_rel;
cb_baton.editor = editor;
cb_baton.edit_baton = edit_baton;
cb_baton.src_kind = src_kind;
cb_baton.src_url = src_url;
cb_baton.src_path = src_rel;
cb_baton.dst_path = dst_rel;
cb_baton.is_move = is_move;
cb_baton.src_revnum = src_revnum;
cb_baton.resurrection = resurrection;
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 err;
}
SVN_ERR(editor->close_edit(edit_baton, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
remove_tmpfiles(apr_hash_t *tempfiles,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
if (! tempfiles)
return SVN_NO_ERROR;
for (hi = apr_hash_first(pool, tempfiles); hi; hi = apr_hash_next(hi))
{
const void *key;
apr_ssize_t keylen;
void *val;
svn_node_kind_t kind;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
apr_hash_this(hi, &key, &keylen, &val);
SVN_ERR(svn_io_check_path((const char *)key, &kind, pool));
if (kind == svn_node_file)
SVN_ERR(svn_io_remove_file((const char *)key, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
reconcile_errors(svn_error_t *commit_err,
svn_error_t *unlock_err,
svn_error_t *cleanup_err,
apr_pool_t *pool)
{
svn_error_t *err;
if (! (commit_err || unlock_err || cleanup_err))
return SVN_NO_ERROR;
if (commit_err)
{
commit_err = svn_error_quick_wrap
(commit_err, _("Commit failed (details follow):"));
err = commit_err;
}
else
err = svn_error_create(SVN_ERR_BASE, NULL,
_("Commit succeeded, but other errors follow:"));
if (unlock_err)
{
unlock_err = svn_error_quick_wrap
(unlock_err, _("Error unlocking locked dirs (details follow):"));
svn_error_compose(err, unlock_err);
}
if (cleanup_err)
{
cleanup_err = svn_error_quick_wrap
(cleanup_err, _("Error in post-commit clean-up (details follow):"));
svn_error_compose(err, cleanup_err);
}
return err;
}
static svn_error_t *
wc_to_repos_copy(svn_commit_info_t **commit_info_p,
const char *src_path,
const char *dst_url,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *anchor, *target, *message;
svn_ra_session_t *ra_session;
const svn_delta_editor_t *editor;
void *edit_baton;
svn_node_kind_t src_kind, dst_kind;
void *commit_baton;
apr_hash_t *committables, *tempfiles = NULL;
svn_wc_adm_access_t *adm_access, *dir_access;
apr_array_header_t *commit_items;
svn_error_t *cmt_err = SVN_NO_ERROR;
svn_error_t *unlock_err = SVN_NO_ERROR;
svn_error_t *cleanup_err = SVN_NO_ERROR;
const char *base_path;
SVN_ERR(svn_path_get_absolute(&base_path, src_path, pool));
SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, base_path,
FALSE, -1, ctx->cancel_func,
ctx->cancel_baton, pool));
svn_path_split(dst_url, &anchor, &target, pool);
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, anchor,
svn_wc_adm_access_path
(adm_access),
adm_access, NULL, TRUE, TRUE,
ctx, pool));
SVN_ERR(svn_ra_check_path(ra_session, svn_path_uri_decode(target, pool),
SVN_INVALID_REVNUM, &dst_kind, pool));
if (dst_kind != svn_node_none)
{
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists"), dst_url);
}
if (ctx->log_msg_func || ctx->log_msg_func2)
{
svn_client_commit_item2_t *item;
const char *tmp_file;
commit_items = apr_array_make(pool, 1, sizeof(item));
item = apr_pcalloc(pool, sizeof(*item));
item->url = dst_url;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item2_t *) = item;
SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
ctx, pool));
if (! message)
return SVN_NO_ERROR;
}
else
message = "";
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
dir_access = adm_access;
if ((cmt_err = svn_client__get_copy_committables(&committables,
dst_url,
base_path,
dir_access,
ctx,
pool)))
goto cleanup;
if (! ((commit_items = apr_hash_get(committables,
SVN_CLIENT__SINGLE_REPOS_NAME,
APR_HASH_KEY_STRING))))
goto cleanup;
if ((cmt_err = svn_client__condense_commit_items(&dst_url,
commit_items,
pool)))
goto cleanup;
if ((cmt_err = svn_client__open_ra_session_internal(&ra_session, dst_url,
NULL, NULL,
commit_items,
FALSE, FALSE,
ctx, pool)))
goto cleanup;
SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
if ((cmt_err = svn_ra_get_commit_editor2(ra_session, &editor, &edit_baton,
message,
svn_client__commit_callback,
commit_baton,
NULL, TRUE,
pool)))
goto cleanup;
cmt_err = svn_client__do_commit(dst_url, commit_items, adm_access,
editor, edit_baton,
0,
&tempfiles, NULL, ctx, pool);
svn_sleep_for_timestamps();
cleanup:
unlock_err = svn_wc_adm_close(adm_access);
if (tempfiles)
cleanup_err = remove_tmpfiles(tempfiles,
ctx->cancel_func, ctx->cancel_baton,
pool);
return reconcile_errors(cmt_err, unlock_err, cleanup_err, pool);
}
static svn_error_t *
repos_to_wc_copy(const char *src_url,
const svn_opt_revision_t *src_revision,
const char *dst_path,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_ra_session_t *ra_session;
svn_node_kind_t src_kind, dst_kind, dst_parent_kind;
svn_revnum_t src_revnum;
svn_wc_adm_access_t *adm_access;
const char *dst_parent;
const char *src_uuid = NULL, *dst_uuid = NULL;
svn_boolean_t same_repositories;
svn_opt_revision_t revision;
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, src_url, NULL,
NULL, NULL, FALSE, TRUE,
ctx, pool));
revision.kind = src_revision->kind;
revision.value = src_revision->value;
if (revision.kind == svn_opt_revision_unspecified)
revision.kind = svn_opt_revision_head;
SVN_ERR(svn_client__get_revision_number
(&src_revnum, ra_session, &revision, NULL, pool));
SVN_ERR(svn_ra_check_path(ra_session, "", src_revnum, &src_kind, pool));
if (src_kind == svn_node_none)
{
if (SVN_IS_VALID_REVNUM(src_revnum))
return svn_error_createf
(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' not found in revision %ld"),
src_url, src_revnum);
else
return svn_error_createf
(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' not found in head revision"), src_url);
}
SVN_ERR(svn_io_check_path(dst_path, &dst_kind, pool));
if (dst_kind != svn_node_none)
{
return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
_("Path '%s' already exists"),
svn_path_local_style(dst_path, pool));
}
dst_parent = svn_path_dirname(dst_path, pool);
SVN_ERR(svn_io_check_path(svn_path_dirname(dst_path, pool),
&dst_parent_kind, pool));
if (dst_parent_kind != svn_node_dir)
return svn_error_createf(SVN_ERR_WC_NOT_DIRECTORY, NULL,
_("Path '%s' is not a directory"),
svn_path_local_style(dst_parent, pool));
SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, dst_path, TRUE,
0, ctx->cancel_func, ctx->cancel_baton,
pool));
{
const svn_wc_entry_t *ent;
SVN_ERR(svn_wc_entry(&ent, dst_path, adm_access, FALSE, pool));
if (ent && (ent->kind != svn_node_dir) &&
(ent->schedule != svn_wc_schedule_delete))
return svn_error_createf
(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Entry for '%s' exists (though the working file is missing)"),
svn_path_local_style(dst_path, pool));
}
{
svn_error_t *src_err, *dst_err;
const char *parent;
src_err = svn_ra_get_uuid(ra_session, &src_uuid, pool);
if (src_err && src_err->apr_err != SVN_ERR_RA_NO_REPOS_UUID)
return src_err;
svn_path_split(dst_path, &parent, NULL, pool);
dst_err = svn_client_uuid_from_path(&dst_uuid, parent, adm_access,
ctx, 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) ? TRUE : FALSE;
}
if (src_kind == svn_node_dir)
{
SVN_ERR(svn_client__checkout_internal
(NULL, src_url, dst_path, &revision, &revision,
TRUE, FALSE, NULL, ctx, pool));
if ((revision.kind == svn_opt_revision_head) && same_repositories)
{
const svn_wc_entry_t *d_entry;
svn_wc_adm_access_t *dst_access;
SVN_ERR(svn_wc_adm_open3(&dst_access, adm_access, dst_path,
TRUE, -1, ctx->cancel_func,
ctx->cancel_baton, pool));
SVN_ERR(svn_wc_entry(&d_entry, dst_path, dst_access, FALSE, pool));
src_revnum = d_entry->revision;
}
if (same_repositories)
{
SVN_ERR(svn_wc_add2(dst_path, adm_access, src_url, src_revnum,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2, pool));
}
else
{
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Source URL '%s' is from foreign repository; "
"leaving it as a disjoint WC"), src_url);
}
}
else if (src_kind == svn_node_file)
{
apr_file_t *fp;
svn_stream_t *fstream;
svn_revnum_t real_rev;
const char *new_text_path;
apr_hash_t *new_props;
svn_error_t *err;
SVN_ERR(svn_io_open_unique_file2
(&fp, &new_text_path, dst_path, ".tmp",
svn_io_file_del_none, pool));
fstream = svn_stream_from_aprfile2(fp, FALSE, pool);
SVN_ERR(svn_ra_get_file(ra_session, "", src_revnum, fstream, &real_rev,
&new_props, pool));
SVN_ERR(svn_stream_close(fstream));
if (! SVN_IS_VALID_REVNUM(src_revnum))
src_revnum = real_rev;
err = svn_wc_add_repos_file2
(dst_path, adm_access,
new_text_path, NULL, new_props, NULL,
same_repositories ? src_url : NULL,
same_repositories ? src_revnum : SVN_INVALID_REVNUM,
pool);
if (!err && ctx->notify_func2)
{
svn_wc_notify_t *notify = svn_wc_create_notify(dst_path,
svn_wc_notify_add,
pool);
notify->kind = src_kind;
(*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
}
svn_sleep_for_timestamps();
SVN_ERR(err);
}
SVN_ERR(svn_wc_adm_close(adm_access));
return SVN_NO_ERROR;
}
static svn_error_t *
setup_copy(svn_commit_info_t **commit_info_p,
const char *src_path,
const svn_opt_revision_t *src_revision,
const char *dst_path,
svn_boolean_t is_move,
svn_boolean_t force,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_boolean_t src_is_url, dst_is_url;
src_is_url = svn_path_is_url(src_path);
dst_is_url = svn_path_is_url(dst_path);
if (!src_is_url && !dst_is_url
&& svn_path_is_child(src_path, dst_path, pool))
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot copy path '%s' into its own child '%s'"),
svn_path_local_style(src_path, pool),
svn_path_local_style(dst_path, pool));
if (is_move)
{
if (src_is_url == dst_is_url)
{
if (strcmp(src_path, dst_path) == 0)
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot move path '%s' into itself"),
svn_path_local_style(src_path, pool));
}
else
{
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("No support for repos <--> working copy moves"));
}
}
else
{
if (!src_is_url)
{
if (src_revision->kind != svn_opt_revision_unspecified
&& src_revision->kind != svn_opt_revision_working)
{
svn_wc_adm_access_t *adm_access;
const svn_wc_entry_t *entry;
SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL,
src_path, FALSE, 0,
ctx->cancel_func,
ctx->cancel_baton,
pool));
SVN_ERR(svn_wc_entry(&entry, src_path, adm_access, FALSE,
pool));
SVN_ERR(svn_wc_adm_close(adm_access));
if (! entry)
return svn_error_createf
(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
_("'%s' is not under version control"),
svn_path_local_style(src_path, pool));
if (! entry->url)
return svn_error_createf
(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' does not seem to have a URL associated with it"),
svn_path_local_style(src_path, pool));
src_path = entry->url;
src_is_url = TRUE;
}
}
}
if ((! src_is_url) && (! dst_is_url))
{
SVN_ERR(wc_to_wc_copy(src_path, dst_path,
is_move, force,
ctx,
pool));
}
else if ((! src_is_url) && (dst_is_url))
{
SVN_ERR(wc_to_repos_copy(commit_info_p, src_path, dst_path,
ctx, pool));
}
else if ((src_is_url) && (! dst_is_url))
{
SVN_ERR(repos_to_wc_copy(src_path, src_revision,
dst_path, ctx,
pool));
}
else
{
SVN_ERR(repos_to_repos_copy(commit_info_p, src_path, src_revision,
dst_path, ctx, is_move, pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_copy3(svn_commit_info_t **commit_info_p,
const char *src_path,
const svn_opt_revision_t *src_revision,
const char *dst_path,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
return setup_copy(commit_info_p,
src_path, src_revision, dst_path,
FALSE ,
TRUE ,
ctx,
pool);
}
svn_error_t *
svn_client_copy2(svn_commit_info_t **commit_info_p,
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_error_t *err;
err = svn_client_copy3(commit_info_p, src_path, src_revision,
dst_path, ctx, pool);
if (err && (err->apr_err == SVN_ERR_ENTRY_EXISTS
|| err->apr_err == SVN_ERR_FS_ALREADY_EXISTS))
{
const char *src_basename = svn_path_basename(src_path, pool);
svn_error_clear(err);
return svn_client_copy3(commit_info_p, src_path, src_revision,
svn_path_join(dst_path, src_basename, pool),
ctx, pool);
}
return err;
}
svn_error_t *
svn_client_copy(svn_client_commit_info_t **commit_info_p,
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_commit_info_t *commit_info = NULL;
svn_error_t *err;
err = svn_client_copy2(&commit_info, src_path, src_revision, dst_path,
ctx, pool);
*commit_info_p = (svn_client_commit_info_t *) commit_info;
return err;
}
svn_error_t *
svn_client_move4(svn_commit_info_t **commit_info_p,
const char *src_path,
const char *dst_path,
svn_boolean_t force,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const svn_opt_revision_t src_revision
= { svn_opt_revision_unspecified, { 0 } };
return setup_copy(commit_info_p,
src_path, &src_revision, dst_path,
TRUE ,
force,
ctx,
pool);
}
svn_error_t *
svn_client_move3(svn_commit_info_t **commit_info_p,
const char *src_path,
const char *dst_path,
svn_boolean_t force,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_error_t *err;
err = svn_client_move4(commit_info_p, src_path, dst_path, force, ctx, pool);
if (err && (err->apr_err == SVN_ERR_ENTRY_EXISTS
|| err->apr_err == SVN_ERR_FS_ALREADY_EXISTS))
{
const char *src_basename = svn_path_basename(src_path, pool);
svn_error_clear(err);
return svn_client_move4(commit_info_p, src_path,
svn_path_join(dst_path, src_basename, pool),
force, ctx, pool);
}
return err;
}
svn_error_t *
svn_client_move2(svn_client_commit_info_t **commit_info_p,
const char *src_path,
const char *dst_path,
svn_boolean_t force,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_commit_info_t *commit_info = NULL;
svn_error_t *err;
err = svn_client_move3(&commit_info, src_path, dst_path, force, ctx, pool);
*commit_info_p = (svn_client_commit_info_t *) commit_info;
return err;
}
svn_error_t *
svn_client_move(svn_client_commit_info_t **commit_info_p,
const char *src_path,
const svn_opt_revision_t *src_revision,
const char *dst_path,
svn_boolean_t force,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_commit_info_t *commit_info = NULL;
svn_error_t *err;
if (src_revision->kind != svn_opt_revision_unspecified
&& src_revision->kind != svn_opt_revision_head)
{
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot specify revisions (except HEAD) with move operations"));
}
err = setup_copy(&commit_info,
src_path, src_revision, dst_path,
TRUE ,
force,
ctx,
pool);
*commit_info_p = (svn_client_commit_info_t *) commit_info;
return err;
}