#include "svn_wc.h"
#include "lock.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_client.h"
#include "svn_error_codes.h"
#include "svn_path.h"
#include "entries.h"
#include "svn_private_config.h"
#define IGNORE_LOCAL_MOD(expr) \
do { \
svn_error_t *__temp = (expr); \
if (__temp) \
{ \
if (__temp->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD) \
svn_error_clear(__temp); \
else \
return __temp; \
} \
} while (0)
static svn_error_t *
crop_children(svn_wc_adm_access_t *adm_access,
const char *dir_path,
svn_depth_t depth,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
apr_hash_t *entries;
apr_hash_index_t *hi;
svn_wc_adm_access_t *dir_access;
svn_wc_entry_t *dot_entry;
apr_pool_t *subpool = svn_pool_create(pool), *iterpool;
SVN_ERR(svn_wc_adm_retrieve(&dir_access, adm_access, dir_path, subpool));
SVN_ERR(svn_wc_entries_read(&entries, dir_access, TRUE, subpool));
dot_entry = apr_hash_get(entries, SVN_WC_ENTRY_THIS_DIR,
APR_HASH_KEY_STRING);
if (dot_entry->depth > depth)
{
dot_entry->depth = depth;
SVN_ERR(svn_wc__entries_write(entries, dir_access, subpool));
}
iterpool = svn_pool_create(subpool);
for (hi = apr_hash_first(subpool, entries); hi; hi = apr_hash_next(hi))
{
const void *key;
const char *this_path;
void *val;
apr_ssize_t klen;
svn_wc_entry_t *current_entry;
svn_pool_clear(iterpool);
apr_hash_this(hi, &key, &klen, &val);
if (! strcmp(key, SVN_WC_ENTRY_THIS_DIR))
continue;
current_entry = val;
this_path = svn_path_join(dir_path, current_entry->name, iterpool);
if (current_entry->kind == svn_node_file)
{
if (depth == svn_depth_empty)
IGNORE_LOCAL_MOD
(svn_wc_remove_from_revision_control(dir_access,
current_entry->name,
TRUE,
FALSE,
cancel_func,
cancel_baton,
iterpool));
else
continue;
}
else if (current_entry->kind == svn_node_dir)
{
if (current_entry->depth == svn_depth_exclude)
{
if (depth < svn_depth_immediates)
{
svn_wc__entry_remove(entries, current_entry->name);
SVN_ERR(svn_wc__entries_write(entries, dir_access, iterpool));
}
continue;
}
else if (depth < svn_depth_immediates)
{
svn_wc_adm_access_t *child_access;
SVN_ERR(svn_wc_adm_retrieve(&child_access, dir_access,
this_path, iterpool));
IGNORE_LOCAL_MOD
(svn_wc_remove_from_revision_control(child_access,
SVN_WC_ENTRY_THIS_DIR,
TRUE,
FALSE,
cancel_func,
cancel_baton,
iterpool));
}
else
{
SVN_ERR(crop_children(dir_access,
this_path,
svn_depth_empty,
notify_func,
notify_baton,
cancel_func,
cancel_baton,
iterpool));
continue;
}
}
else
{
return svn_error_createf
(SVN_ERR_NODE_UNKNOWN_KIND, NULL, _("Unknown entry kind for '%s'"),
svn_path_local_style(this_path, pool));
}
if (notify_func)
{
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(this_path,
svn_wc_notify_delete,
iterpool);
(*notify_func)(notify_baton, notify, iterpool);
}
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_crop_tree(svn_wc_adm_access_t *anchor,
const char *target,
svn_depth_t depth,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const svn_wc_entry_t *entry;
const char *full_path;
svn_wc_adm_access_t *dir_access;
if (depth == svn_depth_infinity)
return SVN_NO_ERROR;
if (!(depth >= svn_depth_exclude && depth < svn_depth_infinity))
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Can only crop a working copy with a restrictive depth"));
full_path = svn_path_join(svn_wc_adm_access_path(anchor), target, pool);
SVN_ERR(svn_wc_entry(&entry, full_path, anchor, FALSE, pool));
if (!entry || entry->kind != svn_node_dir)
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Can only crop directories"));
if (entry->schedule == svn_wc_schedule_delete)
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot crop '%s': it is going to be removed from repository."
" Try commit instead"),
svn_path_local_style(full_path, pool));
if (depth == svn_depth_exclude)
{
svn_boolean_t entry_in_repos;
const svn_wc_entry_t *parent_entry = NULL;
svn_wc_adm_access_t *p_access;
if (*full_path == 0)
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot exclude current directory"));
if (svn_dirent_is_root(full_path, strlen(full_path)))
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot exclude root directory"));
{
const char *bname, *pname;
svn_error_t *err = NULL;
svn_path_split(full_path, &pname, &bname, pool);
SVN_ERR(svn_wc__adm_retrieve_internal(&p_access, anchor, pname,
pool));
if (! p_access)
err = svn_wc_adm_probe_open3(&p_access, NULL, pname, FALSE, 0,
NULL, NULL, pool);
if (! err)
err = svn_wc_entry(&parent_entry, pname, p_access, FALSE, pool);
if (err)
svn_error_clear(err);
if (entry->url
&& parent_entry
&& (strcmp(entry->url,
svn_path_url_add_component2(parent_entry->url, bname,
pool))))
{
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot crop '%s': it is a switched path"),
svn_path_local_style(full_path, pool));
}
}
entry_in_repos
= ! ((entry->schedule == svn_wc_schedule_add
|| entry->schedule == svn_wc_schedule_replace)
&& ! entry->copied);
if (parent_entry && entry_in_repos
&& (parent_entry->depth > svn_depth_files))
{
svn_wc_entry_t *target_entry;
apr_hash_t *parent_entries;
SVN_ERR(svn_wc_entries_read(&parent_entries, p_access,
TRUE, pool));
target_entry = apr_hash_get(parent_entries,
svn_path_basename(full_path, pool),
APR_HASH_KEY_STRING);
target_entry->depth = svn_depth_exclude;
SVN_ERR(svn_wc__entries_write(parent_entries, anchor, pool));
}
SVN_ERR(svn_wc_adm_retrieve(&dir_access, anchor, full_path, pool));
IGNORE_LOCAL_MOD
(svn_wc_remove_from_revision_control(dir_access,
SVN_WC_ENTRY_THIS_DIR,
TRUE,
FALSE,
cancel_func,
cancel_baton,
pool));
if (notify_func)
{
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(full_path,
svn_wc_notify_delete,
pool);
(*notify_func)(notify_baton, notify, pool);
}
return SVN_NO_ERROR;
}
return crop_children(anchor, full_path, depth,
notify_func, notify_baton,
cancel_func, cancel_baton, pool);
}