📄 copy.c
字号:
/* Figure out the basename that will result from this operation. */
SVN_ERR (ra_lib->check_path (sess, dst_rel, youngest, &dst_kind, pool));
if (dst_kind == svn_node_none)
{
/* do nothing */
}
else if (dst_kind == svn_node_file)
{
/* We disallow the overwriting of files. */
return svn_error_createf (SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists"), dst_rel);
}
else if (dst_kind == svn_node_dir)
{
/* 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. */
svn_node_kind_t attempt_kind;
const char *bname;
bname = svn_path_uri_decode (svn_path_basename (src_url, pool), pool);
dst_rel = svn_path_join (dst_rel, bname, pool);
SVN_ERR (ra_lib->check_path (sess, dst_rel, youngest,
&attempt_kind, pool));
if (attempt_kind != svn_node_none)
return svn_error_createf (SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' already exists"), dst_rel);
}
else
{
return svn_error_createf (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
_("Unrecognized node kind of '%s'"), dst_url);
}
/* Create a new commit item and add it to the array. */
if (ctx->log_msg_func)
{
svn_client_commit_item_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;
(*((svn_client_commit_item_t **) apr_array_push (commit_items))) = 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;
(*((svn_client_commit_item_t **) apr_array_push (commit_items))) =
item;
}
SVN_ERR ((*ctx->log_msg_func) (&message, &tmp_file, commit_items,
ctx->log_msg_baton, pool));
if (! message)
return SVN_NO_ERROR;
}
else
message = "";
/* Fetch RA commit editor. */
SVN_ERR (svn_client__commit_get_baton (&commit_baton, commit_info, pool));
SVN_ERR (ra_lib->get_commit_editor (sess, &editor, &edit_baton, message,
svn_client__commit_callback,
commit_baton, pool));
/* Setup our PATHS for the path-based editor drive. */
APR_ARRAY_PUSH (paths, const char *) = dst_rel;
if (is_move && (! resurrection))
APR_ARRAY_PUSH (paths, const char *) = src_rel;
/* Setup the callback baton. */
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;
/* Call the path-based editor driver. */
err = svn_delta_path_driver (editor, edit_baton, youngest, paths,
path_driver_cb_func, &cb_baton, pool);
if (err)
{
/* At least try to abort the edit (and fs txn) before throwing err. */
svn_error_clear (editor->abort_edit (edit_baton, pool));
return err;
}
/* Close the edit. */
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;
/* Split if there's nothing to be done. */
if (! tempfiles)
return SVN_NO_ERROR;
/* Clean up any tempfiles. */
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;
/* Early release (for good behavior). */
if (! (commit_err || unlock_err || cleanup_err))
return SVN_NO_ERROR;
/* If there was a commit error, start off our error chain with
that. */
if (commit_err)
{
commit_err = svn_error_quick_wrap
(commit_err, _("Commit failed (details follow):"));
err = commit_err;
}
/* Else, create a new "general" error that will lead off the errors
that follow. */
else
err = svn_error_create (SVN_ERR_BASE, NULL,
_("Commit succeeded, but other errors follow:"));
/* If there was an unlock error... */
if (unlock_err)
{
/* Wrap the error with some headers. */
unlock_err = svn_error_quick_wrap
(unlock_err, _("Error unlocking locked dirs (details follow):"));
/* Append this error to the chain. */
svn_error_compose (err, unlock_err);
}
/* If there was a cleanup error... */
if (cleanup_err)
{
/* Wrap the error with some headers. */
cleanup_err = svn_error_quick_wrap
(cleanup_err, _("Error in post-commit clean-up (details follow):"));
/* Append this error to the chain. */
svn_error_compose (err, cleanup_err);
}
return err;
}
static svn_error_t *
wc_to_repos_copy (svn_client_commit_info_t **commit_info,
const char *src_path,
const char *dst_url,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *anchor, *target, *base_name, *message;
void *ra_baton, *session;
svn_ra_plugin_t *ra_lib;
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;
svn_boolean_t commit_in_progress = FALSE;
const char *base_path;
const char *base_url;
/* The commit process uses absolute paths, so we need to open the access
baton using absolute paths, and so we really need to use absolute
paths everywhere. */
SVN_ERR (svn_path_get_absolute (&base_path, src_path, pool));
SVN_ERR (svn_wc_adm_probe_open2 (&adm_access, NULL, base_path,
FALSE, -1, pool));
/* Split the DST_URL into an anchor and target. */
svn_path_split (dst_url, &anchor, &target, pool);
/* Get the RA vtable that matches URL. */
SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, anchor, pool));
/* Open an RA session for the anchor URL. */
SVN_ERR (svn_client__open_ra_session (&session, ra_lib, anchor,
svn_wc_adm_access_path (adm_access),
adm_access, NULL, TRUE, TRUE,
ctx, pool));
/* Figure out the basename that will result from this operation. */
SVN_ERR (ra_lib->check_path (session, svn_path_uri_decode (target, pool),
SVN_INVALID_REVNUM, &dst_kind, pool));
/* BASE_URL defaults to DST_URL. */
base_url = apr_pstrdup (pool, dst_url);
if (dst_kind == svn_node_none)
{
/* DST_URL doesn't exist under its parent URL, so the URL we
will be creating is DST_URL. */
}
else if (dst_kind == svn_node_dir)
{
/* DST_URL is an existing directory URL. The URL we will be
creating, then, is DST_URL+BASENAME. */
svn_path_split (base_path, NULL, &base_name, pool);
base_url = svn_path_url_add_component (base_url, base_name, pool);
}
else
{
/* DST_URL is an existing file, which can't be overwritten or
used as a container, so error out. */
return svn_error_createf (SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("File '%s' already exists"), dst_url);
}
/* Create a new commit item and add it to the array. */
if (ctx->log_msg_func)
{
svn_client_commit_item_t *item;
const char *tmp_file;
commit_items = apr_array_make (pool, 1, sizeof (item));
item = apr_pcalloc (pool, sizeof (*item));
item->url = base_url;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
(*((svn_client_commit_item_t **) apr_array_push (commit_items))) = item;
SVN_ERR ((*ctx->log_msg_func) (&message, &tmp_file, commit_items,
ctx->log_msg_baton, pool));
if (! message)
return SVN_NO_ERROR;
}
else
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
dir_access = adm_access;
if ((cmt_err = svn_client__get_copy_committables (&committables,
base_url,
base_path,
dir_access,
ctx,
pool)))
goto cleanup;
/* ### todo: There should be only one hash entry, which currently
has a hacked name until we have the entries files storing
canonical repository URLs. Then, the hacked name can go away and
be replaced with a entry->repos (or whereever the entry's
canonical repos URL is stored). */
if (! ((commit_items = apr_hash_get (committables,
SVN_CLIENT__SINGLE_REPOS_NAME,
APR_HASH_KEY_STRING))))
goto cleanup;
/* Sort and condense our COMMIT_ITEMS. */
if ((cmt_err = svn_client__condense_commit_items (&base_url,
commit_items,
pool)))
goto cleanup;
/* Open an RA session to BASE_URL. */
if ((cmt_err = svn_client__open_ra_session (&session, ra_lib, base_url,
NULL, NULL, commit_items,
FALSE, FALSE,
ctx, pool)))
goto cleanup;
/* Fetch RA commit editor. */
SVN_ERR (svn_client__commit_get_baton (&commit_baton, commit_info, pool));
if ((cmt_err = ra_lib->get_commit_editor (session, &editor, &edit_baton,
message,
svn_client__commit_callback,
commit_baton, pool)))
goto cleanup;
/* Make a note that we have a commit-in-progress. */
commit_in_progress = TRUE;
/* Perform the commit. */
cmt_err = svn_client__do_commit (base_url, commit_items, adm_access,
editor, edit_baton,
0, /* ### any notify_path_offset needed? */
&tempfiles, ctx, pool);
commit_in_progress = FALSE;
/* Sleep to ensure timestamp integrity. */
svn_sleep_for_timestamps ();
cleanup:
/* Abort the commit if it is still in progress. */
if (commit_in_progress)
svn_error_clear (editor->abort_edit (edit_baton, pool));
/* It's only a read lock, so unlocking is harmless. */
unlock_err = svn_wc_adm_close (adm_access);
/* Remove any outstanding temporary text-base files. */
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)
{
void *ra_baton, *sess;
svn_ra_plugin_t *ra_lib;
svn_node_kind_t src_kind, dst_kind;
svn_revnum_t src_revnum;
svn_wc_adm_access_t *adm_access;
const char *src_uuid = NULL, *dst_uuid = NULL;
svn_boolean_t same_repositories;
svn_opt_revision_t revision;
/* Get the RA vtable that matches URL. */
SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, src_url, pool));
/* Open a repository session to the given URL. We do not (yet) have a
working copy, so we don't have a corresponding path and tempfiles
cannot go into the admin area. */
SVN_ERR (svn_client__open_ra_session (&sess, ra_lib, src_url, NULL,
NULL, NULL, FALSE, TRUE,
ctx, pool));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -