#include <apr_uri.h>
#include "svn_wc.h"
#include "svn_pools.h"
#include "svn_client.h"
#include "svn_hash.h"
#include "svn_types.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_config.h"
#include "client.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
struct handle_external_item_change_baton
{
apr_hash_t *new_desc;
apr_hash_t *old_desc;
const char *parent_dir;
svn_wc_adm_access_t *adm_access;
const char *parent_dir_url;
const char *repos_root_url;
svn_client_ctx_t *ctx;
svn_boolean_t *timestamp_sleep;
svn_boolean_t is_export;
apr_pool_t *pool;
apr_pool_t *iter_pool;
};
static svn_error_t *
relegate_dir_external(const char *path,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_error_t *err = SVN_NO_ERROR;
svn_wc_adm_access_t *adm_access;
SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, path, TRUE, -1, cancel_func,
cancel_baton, pool));
err = svn_wc_remove_from_revision_control(adm_access,
SVN_WC_ENTRY_THIS_DIR,
TRUE, FALSE,
cancel_func,
cancel_baton,
pool);
if (!err || err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
SVN_ERR(svn_wc_adm_close2(adm_access, pool));
if (err && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD))
{
const char *parent_dir;
const char *dirname;
const char *new_path;
svn_error_clear(err);
err = SVN_NO_ERROR;
svn_path_split(path, &parent_dir, &dirname, pool);
SVN_ERR(svn_io_open_uniquely_named(NULL, &new_path,
parent_dir, dirname, ".OLD",
svn_io_file_del_none, pool, pool));
svn_error_clear(svn_io_remove_file(new_path, pool));
SVN_ERR(svn_io_file_rename(path, new_path, pool));
}
return err;
}
static svn_error_t *
switch_dir_external(const char *path,
const char *url,
const svn_opt_revision_t *revision,
const svn_opt_revision_t *peg_revision,
svn_boolean_t *timestamp_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_node_kind_t kind;
svn_error_t *err;
apr_pool_t *subpool = svn_pool_create(pool);
SVN_ERR(svn_io_check_path(path, &kind, pool));
if (kind == svn_node_dir)
{
svn_wc_adm_access_t *adm_access;
const svn_wc_entry_t *entry;
SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, path, TRUE, 0,
ctx->cancel_func, ctx->cancel_baton, subpool));
SVN_ERR(svn_wc_entry(&entry, path, adm_access,
FALSE, subpool));
SVN_ERR(svn_wc_adm_close2(adm_access, subpool));
if (entry && entry->url)
{
if (strcmp(entry->url, url) == 0)
{
SVN_ERR(svn_client__update_internal(NULL, path, revision,
svn_depth_unknown, FALSE,
FALSE, FALSE,
timestamp_sleep, TRUE,
ctx, subpool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
else if (entry->repos)
{
if (! svn_path_is_ancestor(entry->repos, url))
{
const char *repos_root;
svn_ra_session_t *ra_session;
SVN_ERR(svn_client__open_ra_session_internal
(&ra_session, url, NULL, NULL, NULL, FALSE, TRUE,
ctx, subpool));
SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root,
subpool));
err = svn_client_relocate(path, entry->repos, repos_root,
TRUE, ctx, subpool);
if (err
&& (err->apr_err == SVN_ERR_WC_INVALID_RELOCATION
|| (err->apr_err
== SVN_ERR_CLIENT_INVALID_RELOCATION)))
{
svn_error_clear(err);
goto relegate;
}
else if (err)
return err;
}
SVN_ERR(svn_client__switch_internal(NULL, path, url,
peg_revision, revision,
NULL, svn_depth_infinity,
TRUE, timestamp_sleep,
FALSE, FALSE, ctx, subpool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
}
}
relegate:
svn_pool_destroy(subpool);
if (kind == svn_node_dir)
SVN_ERR(relegate_dir_external(path,
ctx->cancel_func,
ctx->cancel_baton,
pool));
else
{
const char *parent = svn_path_dirname(path, pool);
SVN_ERR(svn_io_make_dir_recursively(parent, pool));
}
return svn_client__checkout_internal(NULL, url, path, peg_revision,
revision, NULL,
SVN_DEPTH_INFINITY_OR_FILES(TRUE),
FALSE, FALSE, timestamp_sleep,
ctx, pool);
}
static svn_error_t *
switch_file_external(const char *path,
const char *url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *revision,
svn_wc_adm_access_t *adm_access,
svn_ra_session_t *ra_session,
const char *ra_session_url,
svn_revnum_t ra_revnum,
const char *repos_root_url,
svn_boolean_t *timestamp_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_pool_t *subpool = svn_pool_create(pool);
svn_wc_adm_access_t *target_adm_access;
const char *anchor;
const char *target;
const svn_wc_entry_t *entry;
svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config,
SVN_CONFIG_CATEGORY_CONFIG,
APR_HASH_KEY_STRING) : NULL;
svn_boolean_t use_commit_times;
svn_boolean_t unlink_file = FALSE;
svn_boolean_t revert_file = FALSE;
svn_boolean_t remove_from_revision_control = FALSE;
svn_boolean_t close_adm_access = FALSE;
svn_error_t *err = NULL;
SVN_ERR(svn_wc_get_actual_target(path, &anchor, &target, subpool));
err = svn_wc_adm_retrieve(&target_adm_access, adm_access, anchor, subpool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_NOT_LOCKED)
{
const char *dest_wc_repos_root_url;
svn_opt_revision_t peg_rev;
svn_error_clear(err);
close_adm_access = TRUE;
SVN_ERR(svn_wc_adm_open3(&target_adm_access, NULL, anchor, TRUE, 1,
ctx->cancel_func, ctx->cancel_baton,
subpool));
peg_rev.kind = svn_opt_revision_base;
SVN_ERR(svn_client__get_repos_root(&dest_wc_repos_root_url,
anchor, &peg_rev,
target_adm_access, ctx, subpool));
if (0 != strcmp(repos_root_url, dest_wc_repos_root_url))
return svn_error_createf
(SVN_ERR_RA_REPOS_ROOT_URL_MISMATCH, NULL,
_("Cannot insert a file external from '%s' into a working "
"copy from a different repository rooted at '%s'"),
url, dest_wc_repos_root_url);
}
else
return err;
}
SVN_ERR(svn_wc_entry(&entry, path, target_adm_access, FALSE, subpool));
SVN_ERR(svn_config_get_bool(cfg, &use_commit_times,
SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE));
if (entry)
{
if (! entry->file_external_path)
{
if (close_adm_access)
SVN_ERR(svn_wc_adm_close2(target_adm_access, subpool));
return svn_error_createf
(SVN_ERR_CLIENT_FILE_EXTERNAL_OVERWRITE_VERSIONED, 0,
_("The file external from '%s' cannot overwrite the existing "
"versioned item at '%s'"),
url, path);
}
}
else
{
const svn_wc_entry_t *anchor_dir_entry;
apr_file_t *f;
svn_boolean_t text_conflicted;
svn_boolean_t prop_conflicted;
svn_boolean_t tree_conflicted;
SVN_ERR(svn_wc__entry_versioned(&anchor_dir_entry, anchor,
target_adm_access, FALSE, subpool));
SVN_ERR(svn_wc_conflicted_p2(&text_conflicted, &prop_conflicted,
&tree_conflicted, anchor, target_adm_access,
subpool));
if (text_conflicted || prop_conflicted || tree_conflicted)
return svn_error_createf
(SVN_ERR_WC_FOUND_CONFLICT, 0,
_("The file external from '%s' cannot be written to '%s' while "
"'%s' remains in conflict"),
url, path, anchor);
SVN_ERR(svn_io_file_open(&f,
path,
APR_WRITE | APR_CREATE | APR_EXCL,
APR_OS_DEFAULT,
subpool));
unlink_file = TRUE;
err = svn_io_file_close(f, pool);
if (err)
goto cleanup;
err = svn_wc_add3(path, target_adm_access, svn_depth_infinity,
NULL,
SVN_INVALID_REVNUM,
ctx->cancel_func, ctx->cancel_baton,
NULL,
NULL,
subpool);
if (err)
goto cleanup;
revert_file = TRUE;
err = svn_wc__set_file_external_location(target_adm_access, target,
url, peg_revision, revision,
repos_root_url, subpool);
if (err)
goto cleanup;
}
err = svn_client__switch_internal(NULL, path, url, peg_revision, revision,
target_adm_access, svn_depth_empty,
FALSE,
timestamp_sleep,
TRUE,
FALSE,
ctx,
pool);
if (err)
goto cleanup;
if (unlink_file)
{
revert_file = FALSE;
remove_from_revision_control = TRUE;
if (err)
goto cleanup;
}
if (close_adm_access)
SVN_ERR(svn_wc_adm_close2(target_adm_access, subpool));
return SVN_NO_ERROR;
cleanup:
if (revert_file)
{
svn_error_t *e =
svn_wc_revert3(path, target_adm_access, svn_depth_empty,
use_commit_times,
NULL,
ctx->cancel_func,
ctx->cancel_baton,
NULL,
NULL,
subpool);
if (e)
svn_error_clear(e);
}
if (remove_from_revision_control)
{
svn_error_t * e = svn_wc_remove_from_revision_control(target_adm_access,
target,
TRUE, FALSE,
ctx->cancel_func,
ctx->cancel_baton,
subpool);
if (e)
svn_error_clear(e);
}
if (unlink_file)
{
svn_error_t *e = svn_io_remove_file(path, subpool);
if (e)
svn_error_clear(e);
}
if (close_adm_access)
SVN_ERR(svn_wc_adm_close2(target_adm_access, subpool));
return err;
}
static svn_error_t *
uri_scheme(const char **scheme, const char *uri, apr_pool_t *pool)
{
apr_size_t i;
for (i = 0; uri[i] && uri[i] != ':'; ++i)
if (uri[i] == '/')
goto error;
if (i > 0 && uri[i] == ':' && uri[i+1] == '/' && uri[i+2] == '/')
{
*scheme = apr_pstrmemdup(pool, uri, i);
return SVN_NO_ERROR;
}
error:
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("URL '%s' does not begin with a scheme"),
uri);
}
static svn_error_t *
resolve_relative_external_url(svn_wc_external_item2_t *item,
const char *repos_root_url,
const char *parent_dir_url,
apr_pool_t *pool)
{
const char *uncanonicalized_url = item->url;
const char *canonicalized_url;
apr_uri_t parent_dir_parsed_uri;
apr_status_t status;
canonicalized_url = svn_path_canonicalize(uncanonicalized_url, pool);
if (svn_path_is_url(canonicalized_url))
{
item->url = canonicalized_url;
return SVN_NO_ERROR;
}
status = apr_uri_parse(pool, parent_dir_url, &parent_dir_parsed_uri);
if (status)
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("Illegal parent directory URL '%s'"),
parent_dir_url);
if (! parent_dir_parsed_uri.path)
parent_dir_parsed_uri.path = apr_pstrmemdup(pool, "/", 1);
if ((0 == strncmp("../", uncanonicalized_url, 3)) ||
(0 == strncmp("^/", uncanonicalized_url, 2)))
{
apr_array_header_t *base_components;
apr_array_header_t *relative_components;
int i;
if (0 == strncmp("../", uncanonicalized_url, 3))
{
base_components = svn_path_decompose(parent_dir_parsed_uri.path,
pool);
relative_components = svn_path_decompose(canonicalized_url, pool);
}
else
{
apr_uri_t repos_root_parsed_uri;
status = apr_uri_parse(pool, repos_root_url, &repos_root_parsed_uri);
if (status)
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("Illegal repository root URL '%s'"),
repos_root_url);
if (! repos_root_parsed_uri.path)
repos_root_parsed_uri.path = apr_pstrmemdup(pool, "/", 1);
base_components = svn_path_decompose(repos_root_parsed_uri.path,
pool);
relative_components = svn_path_decompose(canonicalized_url + 2,
pool);
}
for (i = 0; i < relative_components->nelts; ++i)
{
const char *component = APR_ARRAY_IDX(relative_components,
i,
const char *);
if (0 == strcmp("..", component))
{
if (base_components->nelts > 1)
apr_array_pop(base_components);
}
else
APR_ARRAY_PUSH(base_components, const char *) = component;
}
parent_dir_parsed_uri.path = (char *)svn_path_compose(base_components,
pool);
parent_dir_parsed_uri.query = NULL;
parent_dir_parsed_uri.fragment = NULL;
item->url = apr_uri_unparse(pool, &parent_dir_parsed_uri, 0);
return SVN_NO_ERROR;
}
if (svn_path_is_backpath_present(canonicalized_url + 2))
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("The external relative URL '%s' cannot have "
"backpaths, i.e. '..'"),
uncanonicalized_url);
if (0 == strncmp("//", uncanonicalized_url, 2))
{
const char *scheme;
SVN_ERR(uri_scheme(&scheme, repos_root_url, pool));
item->url = svn_path_canonicalize(apr_pstrcat(pool,
scheme,
":",
uncanonicalized_url,
NULL),
pool);
return SVN_NO_ERROR;
}
if (uncanonicalized_url[0] == '/')
{
parent_dir_parsed_uri.path = (char *)canonicalized_url;
parent_dir_parsed_uri.query = NULL;
parent_dir_parsed_uri.fragment = NULL;
item->url = apr_uri_unparse(pool, &parent_dir_parsed_uri, 0);
return SVN_NO_ERROR;
}
return svn_error_createf(SVN_ERR_BAD_URL, 0,
_("Unrecognized format for the relative external "
"URL '%s'"),
uncanonicalized_url);
}
static svn_error_t *
handle_external_item_change(const void *key, apr_ssize_t klen,
enum svn_hash_diff_key_status status,
void *baton)
{
struct handle_external_item_change_baton *ib = baton;
svn_wc_external_item2_t *old_item, *new_item;
const char *parent;
const char *path = svn_path_join(ib->parent_dir,
(const char *) key, ib->iter_pool);
svn_ra_session_t *ra_session;
svn_node_kind_t kind;
svn_client__ra_session_from_path_results ra_cache = { 0 };
if ((ib->old_desc) && (! ib->is_export))
{
old_item = apr_hash_get(ib->old_desc, key, klen);
if (old_item)
SVN_ERR(resolve_relative_external_url(old_item, ib->repos_root_url,
ib->parent_dir_url,
ib->pool));
}
else
old_item = NULL;
if (ib->new_desc)
{
new_item = apr_hash_get(ib->new_desc, key, klen);
if (new_item)
SVN_ERR(resolve_relative_external_url(new_item, ib->repos_root_url,
ib->parent_dir_url,
ib->pool));
}
else
new_item = NULL;
SVN_ERR_ASSERT(old_item || new_item);
if (new_item)
{
SVN_ERR(svn_client__ra_session_from_path(&ra_session,
&ra_cache.ra_revnum,
&ra_cache.ra_session_url,
new_item->url, NULL,
&(new_item->peg_revision),
&(new_item->revision), ib->ctx,
ib->iter_pool));
SVN_ERR(svn_ra_get_uuid2(ra_session, &ra_cache.repos_uuid,
ib->iter_pool));
SVN_ERR(svn_ra_get_repos_root2(ra_session, &ra_cache.repos_root_url,
ib->iter_pool));
SVN_ERR(svn_ra_check_path(ra_session, "", ra_cache.ra_revnum, &kind,
ib->iter_pool));
if (svn_node_none == kind)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("URL '%s' at revision %ld doesn't exist"),
ra_cache.ra_session_url,
ra_cache.ra_revnum);
if (svn_node_dir != kind && svn_node_file != kind)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("URL '%s' at revision %ld is not a file "
"or a directory"),
ra_cache.ra_session_url,
ra_cache.ra_revnum);
ra_cache.kind_p = &kind;
}
if (! old_item)
{
if (ib->ctx->notify_func2)
(*ib->ctx->notify_func2)
(ib->ctx->notify_baton2,
svn_wc_create_notify(path, svn_wc_notify_update_external,
ib->iter_pool), ib->iter_pool);
switch (*ra_cache.kind_p)
{
case svn_node_dir:
svn_path_split(path, &parent, NULL, ib->iter_pool);
SVN_ERR(svn_io_make_dir_recursively(parent, ib->iter_pool));
if (ib->is_export)
SVN_ERR(svn_client_export4(NULL, new_item->url, path,
&(new_item->peg_revision),
&(new_item->revision),
TRUE, FALSE, svn_depth_infinity, NULL,
ib->ctx, ib->iter_pool));
else
SVN_ERR(svn_client__checkout_internal
(NULL, new_item->url, path,
&(new_item->peg_revision), &(new_item->revision),
&ra_cache,
SVN_DEPTH_INFINITY_OR_FILES(TRUE),
FALSE, FALSE, ib->timestamp_sleep, ib->ctx,
ib->iter_pool));
break;
case svn_node_file:
if (ib->is_export)
SVN_ERR(svn_client_export4(NULL, new_item->url, path,
&(new_item->peg_revision),
&(new_item->revision),
FALSE, TRUE, svn_depth_infinity, NULL,
ib->ctx, ib->iter_pool));
else
SVN_ERR(switch_file_external(path,
new_item->url,
&new_item->peg_revision,
&new_item->revision,
ib->adm_access,
ra_session,
ra_cache.ra_session_url,
ra_cache.ra_revnum,
ra_cache.repos_root_url,
ib->timestamp_sleep, ib->ctx,
ib->iter_pool));
break;
default:
SVN_ERR_MALFUNCTION();
break;
}
}
else if (! new_item)
{
svn_error_t *err;
svn_wc_adm_access_t *adm_access;
svn_boolean_t close_access_baton_when_done;
const char *what_to_remove;
err = svn_wc_adm_open3(&adm_access, NULL, path, TRUE, -1,
ib->ctx->cancel_func, ib->ctx->cancel_baton,
ib->iter_pool);
if (err)
{
const char *anchor;
const char *target;
svn_error_t *err2;
SVN_ERR(svn_wc_get_actual_target(path, &anchor, &target,
ib->iter_pool));
err2 = svn_wc_adm_retrieve(&adm_access, ib->adm_access, anchor,
ib->iter_pool);
if (err2)
{
svn_error_clear(err2);
return err;
}
else
{
svn_error_clear(err);
}
close_access_baton_when_done = FALSE;
what_to_remove = target;
}
else
{
close_access_baton_when_done = TRUE;
what_to_remove = SVN_WC_ENTRY_THIS_DIR;
}
err = svn_wc_remove_from_revision_control
(adm_access, what_to_remove, TRUE, FALSE,
ib->ctx->cancel_func, ib->ctx->cancel_baton, ib->iter_pool);
if (close_access_baton_when_done &&
(!err || err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD))
{
svn_error_t *err2 = svn_wc_adm_close2(adm_access, ib->iter_pool);
if (err2)
{
if (!err)
err = err2;
else
svn_error_clear(err2);
}
}
if (err && (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD))
return err;
svn_error_clear(err);
}
else
{
if (ib->ctx->notify_func2)
(*ib->ctx->notify_func2)
(ib->ctx->notify_baton2,
svn_wc_create_notify(path, svn_wc_notify_update_external,
ib->iter_pool), ib->iter_pool);
switch (*ra_cache.kind_p)
{
case svn_node_dir:
SVN_ERR(switch_dir_external(path, new_item->url,
&(new_item->revision),
&(new_item->peg_revision),
ib->timestamp_sleep, ib->ctx,
ib->iter_pool));
break;
case svn_node_file:
SVN_ERR(switch_file_external(path,
new_item->url,
&new_item->peg_revision,
&new_item->revision,
ib->adm_access,
ra_session,
ra_cache.ra_session_url,
ra_cache.ra_revnum,
ra_cache.repos_root_url,
ib->timestamp_sleep, ib->ctx,
ib->iter_pool));
break;
default:
SVN_ERR_MALFUNCTION();
break;
}
}
svn_pool_clear(ib->iter_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
handle_external_item_change_wrapper(const void *key, apr_ssize_t klen,
enum svn_hash_diff_key_status status,
void *baton)
{
struct handle_external_item_change_baton *ib = baton;
svn_error_t *err = handle_external_item_change(key, klen, status, baton);
if (err && ib->ctx->notify_func2)
{
const char *path = svn_path_join(ib->parent_dir, key, ib->iter_pool);
svn_wc_notify_t *notifier =
svn_wc_create_notify(path,
svn_wc_notify_failed_external,
ib->pool);
notifier->err = err;
ib->ctx->notify_func2(ib->ctx->notify_baton2, notifier, ib->pool);
}
svn_error_clear(err);
return SVN_NO_ERROR;
}
struct handle_externals_desc_change_baton
{
apr_hash_t *externals_new;
apr_hash_t *externals_old;
svn_depth_t requested_depth;
apr_hash_t *ambient_depths;
const char *from_url;
const char *to_path;
svn_wc_adm_access_t *adm_access;
svn_client_ctx_t *ctx;
const char *repos_root_url;
svn_boolean_t *timestamp_sleep;
svn_boolean_t is_export;
apr_pool_t *pool;
};
static svn_error_t *
handle_externals_desc_change(const void *key, apr_ssize_t klen,
enum svn_hash_diff_key_status status,
void *baton)
{
struct handle_externals_desc_change_baton *cb = baton;
struct handle_external_item_change_baton ib = { 0 };
const char *old_desc_text, *new_desc_text;
apr_array_header_t *old_desc, *new_desc;
apr_hash_t *old_desc_hash, *new_desc_hash;
apr_size_t len;
int i;
svn_wc_external_item2_t *item;
const char *ambient_depth_w;
svn_depth_t ambient_depth;
if (cb->is_export)
SVN_ERR_ASSERT(!cb->adm_access);
else
SVN_ERR_ASSERT(cb->adm_access);
if (cb->ambient_depths)
{
ambient_depth_w = apr_hash_get(cb->ambient_depths, key, klen);
if (ambient_depth_w == NULL)
{
return svn_error_createf
(SVN_ERR_WC_CORRUPT, NULL,
_("Traversal of '%s' found no ambient depth"),
(const char *) key);
}
else
{
ambient_depth = svn_depth_from_word(ambient_depth_w);
}
}
else
{
ambient_depth = svn_depth_infinity;
}
if ((cb->requested_depth < svn_depth_infinity
&& cb->requested_depth != svn_depth_unknown)
|| (ambient_depth < svn_depth_infinity
&& cb->requested_depth < svn_depth_infinity))
return SVN_NO_ERROR;
if ((old_desc_text = apr_hash_get(cb->externals_old, key, klen)))
SVN_ERR(svn_wc_parse_externals_description3(&old_desc, key, old_desc_text,
FALSE, cb->pool));
else
old_desc = NULL;
if ((new_desc_text = apr_hash_get(cb->externals_new, key, klen)))
SVN_ERR(svn_wc_parse_externals_description3(&new_desc, key, new_desc_text,
FALSE, cb->pool));
else
new_desc = NULL;
old_desc_hash = apr_hash_make(cb->pool);
new_desc_hash = apr_hash_make(cb->pool);
for (i = 0; old_desc && (i < old_desc->nelts); i++)
{
item = APR_ARRAY_IDX(old_desc, i, svn_wc_external_item2_t *);
apr_hash_set(old_desc_hash, item->target_dir,
APR_HASH_KEY_STRING, item);
}
for (i = 0; new_desc && (i < new_desc->nelts); i++)
{
item = APR_ARRAY_IDX(new_desc, i, svn_wc_external_item2_t *);
apr_hash_set(new_desc_hash, item->target_dir,
APR_HASH_KEY_STRING, item);
}
ib.old_desc = old_desc_hash;
ib.new_desc = new_desc_hash;
ib.parent_dir = (const char *) key;
ib.repos_root_url = cb->repos_root_url;
ib.adm_access = cb->adm_access;
ib.ctx = cb->ctx;
ib.is_export = cb->is_export;
ib.timestamp_sleep = cb->timestamp_sleep;
ib.pool = cb->pool;
ib.iter_pool = svn_pool_create(cb->pool);
len = strlen(cb->to_path);
if (ib.parent_dir[len] == '/')
++len;
ib.parent_dir_url = svn_path_url_add_component2(cb->from_url,
ib.parent_dir + len,
cb->pool);
for (i = 0; old_desc && (i < old_desc->nelts); i++)
{
item = APR_ARRAY_IDX(old_desc, i, svn_wc_external_item2_t *);
if (apr_hash_get(new_desc_hash, item->target_dir, APR_HASH_KEY_STRING))
SVN_ERR(handle_external_item_change_wrapper(item->target_dir,
APR_HASH_KEY_STRING,
svn_hash_diff_key_both,
&ib));
else
SVN_ERR(handle_external_item_change_wrapper(item->target_dir,
APR_HASH_KEY_STRING,
svn_hash_diff_key_a,
&ib));
}
for (i = 0; new_desc && (i < new_desc->nelts); i++)
{
item = APR_ARRAY_IDX(new_desc, i, svn_wc_external_item2_t *);
if (! apr_hash_get(old_desc_hash, item->target_dir, APR_HASH_KEY_STRING))
SVN_ERR(handle_external_item_change_wrapper(item->target_dir,
APR_HASH_KEY_STRING,
svn_hash_diff_key_b,
&ib));
}
svn_pool_destroy(ib.iter_pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__handle_externals(svn_wc_adm_access_t *adm_access,
svn_wc_traversal_info_t *traversal_info,
const char *from_url,
const char *to_path,
const char *repos_root_url,
svn_depth_t requested_depth,
svn_boolean_t *timestamp_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_hash_t *externals_old, *externals_new, *ambient_depths;
struct handle_externals_desc_change_baton cb = { 0 };
svn_wc_edited_externals(&externals_old, &externals_new, traversal_info);
svn_wc_traversed_depths(&ambient_depths, traversal_info);
if (! svn_path_is_url(from_url))
return svn_error_createf
(SVN_ERR_BAD_URL, NULL, _("'%s' is not a URL"), from_url);
cb.externals_new = externals_new;
cb.externals_old = externals_old;
cb.requested_depth = requested_depth;
cb.ambient_depths = ambient_depths;
cb.from_url = from_url;
cb.to_path = to_path;
cb.repos_root_url = repos_root_url;
cb.adm_access = adm_access;
cb.ctx = ctx;
cb.timestamp_sleep = timestamp_sleep;
cb.is_export = FALSE;
cb.pool = pool;
return svn_hash_diff(cb.externals_old, cb.externals_new,
handle_externals_desc_change, &cb, pool);
}
svn_error_t *
svn_client__fetch_externals(apr_hash_t *externals,
const char *from_url,
const char *to_path,
const char *repos_root_url,
svn_depth_t requested_depth,
svn_boolean_t is_export,
svn_boolean_t *timestamp_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
struct handle_externals_desc_change_baton cb = { 0 };
cb.externals_new = externals;
cb.externals_old = apr_hash_make(pool);
cb.requested_depth = requested_depth;
cb.ambient_depths = NULL;
cb.adm_access = NULL;
cb.ctx = ctx;
cb.from_url = from_url;
cb.to_path = to_path;
cb.repos_root_url = repos_root_url;
cb.timestamp_sleep = timestamp_sleep;
cb.is_export = is_export;
cb.pool = pool;
return svn_hash_diff(cb.externals_old, cb.externals_new,
handle_externals_desc_change, &cb, pool);
}
svn_error_t *
svn_client__do_external_status(svn_wc_traversal_info_t *traversal_info,
svn_wc_status_func3_t status_func,
void *status_baton,
svn_depth_t depth,
svn_boolean_t get_all,
svn_boolean_t update,
svn_boolean_t no_ignore,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_hash_t *externals_old, *externals_new;
apr_hash_index_t *hi;
apr_pool_t *subpool = svn_pool_create(pool);
svn_wc_edited_externals(&externals_old, &externals_new, traversal_info);
for (hi = apr_hash_first(pool, externals_new);
hi;
hi = apr_hash_next(hi))
{
apr_array_header_t *exts;
const void *key;
void *val;
const char *path;
const char *propval;
apr_pool_t *iterpool;
int i;
svn_pool_clear(subpool);
apr_hash_this(hi, &key, NULL, &val);
path = key;
propval = val;
SVN_ERR(svn_wc_parse_externals_description3(&exts, path, propval,
FALSE, subpool));
iterpool = svn_pool_create(subpool);
for (i = 0; exts && (i < exts->nelts); i++)
{
const char *fullpath;
svn_wc_external_item2_t *external;
svn_node_kind_t kind;
svn_pool_clear(iterpool);
external = APR_ARRAY_IDX(exts, i, svn_wc_external_item2_t *);
fullpath = svn_path_join(path, external->target_dir, iterpool);
SVN_ERR(svn_io_check_path(fullpath, &kind, iterpool));
if (kind != svn_node_dir)
continue;
if (ctx->notify_func2)
(ctx->notify_func2)
(ctx->notify_baton2,
svn_wc_create_notify(fullpath, svn_wc_notify_status_external,
iterpool), iterpool);
SVN_ERR(svn_client_status4(NULL, fullpath,
&(external->revision),
status_func, status_baton,
depth, get_all, update,
no_ignore, FALSE, NULL, ctx, iterpool));
}
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}