#include "svn_client.h"
#include "svn_error.h"
#include "svn_time.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_config.h"
#include "svn_pools.h"
#include "client.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
static svn_error_t *
switch_internal(svn_revnum_t *result_rev,
const char *local_abspath,
const char *anchor_abspath,
const char *switch_url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *revision,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
svn_boolean_t ignore_externals,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t ignore_ancestry,
svn_boolean_t *timestamp_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const svn_ra_reporter3_t *reporter;
void *report_baton;
const char *url, *target, *source_root, *switch_rev_url;
svn_ra_session_t *ra_session;
svn_revnum_t revnum;
svn_error_t *err = SVN_NO_ERROR;
const char *diff3_cmd;
svn_boolean_t use_commit_times;
svn_boolean_t sleep_here = FALSE;
svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here;
const svn_delta_editor_t *switch_editor;
void *switch_edit_baton;
const char *preserved_exts_str;
apr_array_header_t *preserved_exts;
svn_boolean_t server_supports_depth;
struct svn_client__dirent_fetcher_baton_t dfb;
svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config,
SVN_CONFIG_CATEGORY_CONFIG,
APR_HASH_KEY_STRING)
: NULL;
if (depth == svn_depth_unknown)
depth_is_sticky = FALSE;
if (depth == svn_depth_exclude)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot both exclude and switch a path"));
svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
if (diff3_cmd != NULL)
SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool));
SVN_ERR(svn_config_get_bool(cfg, &use_commit_times,
SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE));
{
svn_boolean_t has_working;
SVN_ERR(svn_wc__node_has_working(&has_working, ctx->wc_ctx, local_abspath,
pool));
if (has_working)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot switch '%s' because it is not in the "
"repository yet"),
svn_dirent_local_style(local_abspath, pool));
}
svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, "");
preserved_exts = *preserved_exts_str
? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, pool)
: NULL;
SVN_ERR_ASSERT(switch_url && (switch_url[0] != '\0'));
if (strcmp(local_abspath, anchor_abspath))
target = svn_dirent_basename(local_abspath, pool);
else
target = "";
SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, anchor_abspath, pool, pool));
if (! url)
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("Directory '%s' has no URL"),
svn_dirent_local_style(anchor_abspath, pool));
if (depth_is_sticky && depth < svn_depth_infinity)
{
svn_node_kind_t target_kind;
if (depth == svn_depth_exclude)
{
SVN_ERR(svn_wc_exclude(ctx->wc_ctx,
local_abspath,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2,
pool));
return SVN_NO_ERROR;
}
SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, local_abspath, TRUE,
pool));
if (target_kind == svn_node_dir)
SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2,
pool));
}
SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum,
&switch_rev_url,
switch_url, anchor_abspath,
peg_revision, revision,
ctx, pool));
SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root, pool));
if (! svn_uri__is_ancestor(source_root, url))
return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
_("'%s'\nis not the same repository as\n'%s'"),
url, source_root);
if (! ignore_ancestry)
{
const char *target_url, *yc_path;
svn_revnum_t target_rev, yc_rev;
SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, local_abspath,
pool, pool));
SVN_ERR(svn_wc__node_get_base_rev(&target_rev, ctx->wc_ctx,
local_abspath, pool));
SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_path, &yc_rev,
switch_rev_url, revnum,
target_url, target_rev,
ctx, pool));
if (! (yc_path && SVN_IS_VALID_REVNUM(yc_rev)))
return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
_("'%s' shares no common ancestry with '%s'"),
switch_url, local_abspath);
}
SVN_ERR(svn_ra_reparent(ra_session, url, pool));
SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
SVN_RA_CAPABILITY_DEPTH, pool));
dfb.ra_session = ra_session;
SVN_ERR(svn_ra_get_session_url(ra_session, &dfb.anchor_url, pool));
dfb.target_revision = revnum;
SVN_ERR(svn_wc_get_switch_editor4(&switch_editor, &switch_edit_baton,
&revnum, ctx->wc_ctx, anchor_abspath,
target, switch_rev_url, use_commit_times,
depth,
depth_is_sticky, allow_unver_obstructions,
server_supports_depth,
diff3_cmd, preserved_exts,
svn_client__dirent_fetcher, &dfb,
ctx->conflict_func2, ctx->conflict_baton2,
NULL, NULL,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2,
pool, pool));
SVN_ERR(svn_ra_do_switch2(ra_session, &reporter, &report_baton, revnum,
target,
depth_is_sticky ? depth : svn_depth_unknown,
switch_rev_url,
switch_editor, switch_edit_baton, pool));
err = svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter,
report_baton, TRUE, depth, (! depth_is_sticky),
(! server_supports_depth),
use_commit_times,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2, pool);
if (err)
{
svn_io_sleep_for_timestamps(local_abspath, pool);
return svn_error_trace(err);
}
*use_sleep = TRUE;
if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
{
apr_hash_t *new_externals;
apr_hash_t *new_depths;
SVN_ERR(svn_wc__externals_gather_definitions(&new_externals,
&new_depths,
ctx->wc_ctx, local_abspath,
depth, pool, pool));
SVN_ERR(svn_client__handle_externals(new_externals,
new_depths,
source_root, local_abspath,
depth, use_sleep,
ctx, pool));
}
if (sleep_here)
svn_io_sleep_for_timestamps(local_abspath, pool);
if (err)
return svn_error_trace(err);
if (ctx->notify_func2)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(anchor_abspath, svn_wc_notify_update_completed,
pool);
notify->kind = svn_node_none;
notify->content_state = notify->prop_state
= svn_wc_notify_state_inapplicable;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
notify->revision = revnum;
(*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
}
if (result_rev)
*result_rev = revnum;
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__switch_internal(svn_revnum_t *result_rev,
const char *path,
const char *switch_url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *revision,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
svn_boolean_t ignore_externals,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t ignore_ancestry,
svn_boolean_t *timestamp_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *local_abspath, *anchor_abspath;
svn_boolean_t acquired_lock;
svn_error_t *err, *err1, *err2;
SVN_ERR_ASSERT(path);
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
err = svn_wc__acquire_write_lock(&anchor_abspath,
ctx->wc_ctx, local_abspath, TRUE,
pool, pool);
if (err && err->apr_err != SVN_ERR_WC_LOCKED)
return svn_error_trace(err);
acquired_lock = (err == SVN_NO_ERROR);
svn_error_clear(err);
err1 = switch_internal(result_rev, local_abspath, anchor_abspath,
switch_url, peg_revision, revision,
depth, depth_is_sticky,
ignore_externals,
allow_unver_obstructions, ignore_ancestry,
timestamp_sleep, ctx, pool);
if (acquired_lock)
err2 = svn_wc__release_write_lock(ctx->wc_ctx, anchor_abspath, pool);
else
err2 = SVN_NO_ERROR;
return svn_error_compose_create(err1, err2);
}
svn_error_t *
svn_client_switch3(svn_revnum_t *result_rev,
const char *path,
const char *switch_url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *revision,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
svn_boolean_t ignore_externals,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t ignore_ancestry,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
if (svn_path_is_url(path))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' is not a local path"), path);
return svn_client__switch_internal(result_rev, path, switch_url,
peg_revision, revision, depth,
depth_is_sticky, ignore_externals,
allow_unver_obstructions,
ignore_ancestry, NULL, ctx, pool);
}