#include <assert.h>
#include <string.h>
#include <apr_pools.h>
#include <apr_file_io.h>
#include <apr_hash.h>
#include "svn_pools.h"
#include "svn_types.h"
#include "svn_delta.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_io.h"
#include "svn_config.h"
#include "svn_time.h"
#include "svn_hash.h"
#include "svn_sorts.h"
#include "svn_private_config.h"
#include "wc.h"
#include "props.h"
#include "entries.h"
#include "translate.h"
#include "tree_conflicts.h"
#include "private/svn_wc_private.h"
#include "private/svn_fspath.h"
struct walk_status_baton
{
svn_wc__db_t *db;
const char *target_abspath;
svn_boolean_t ignore_text_mods;
apr_hash_t *externals;
const char *repos_root;
apr_hash_t *repos_locks;
};
struct edit_baton
{
const char *anchor_abspath;
const char *target_abspath;
const char *target_basename;
svn_wc__db_t *db;
svn_wc_context_t *wc_ctx;
svn_depth_t default_depth;
svn_boolean_t get_all;
svn_boolean_t no_ignore;
svn_revnum_t *target_revision;
svn_wc_status_func4_t status_func;
void *status_baton;
svn_cancel_func_t cancel_func;
void *cancel_baton;
const apr_array_header_t *ignores;
svn_wc_status3_t *anchor_status;
svn_boolean_t root_opened;
struct walk_status_baton wb;
};
struct dir_baton
{
const char *local_abspath;
const char *name;
struct edit_baton *edit_baton;
struct dir_baton *parent_baton;
svn_depth_t depth;
svn_boolean_t excluded;
svn_boolean_t added;
svn_boolean_t prop_changed;
svn_boolean_t text_changed;
apr_hash_t *statii;
apr_pool_t *pool;
const char *repos_relpath;
svn_node_kind_t ood_kind;
svn_revnum_t ood_changed_rev;
apr_time_t ood_changed_date;
const char *ood_changed_author;
};
struct file_baton
{
const char *local_abspath;
struct edit_baton *edit_baton;
struct dir_baton *dir_baton;
apr_pool_t *pool;
const char *name;
svn_boolean_t added;
svn_boolean_t text_changed;
svn_boolean_t prop_changed;
const char *repos_relpath;
svn_node_kind_t ood_kind;
svn_revnum_t ood_changed_rev;
apr_time_t ood_changed_date;
const char *ood_changed_author;
};
static svn_error_t *
read_info(const struct svn_wc__db_info_t **info,
const char *local_abspath,
svn_wc__db_t *db,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct svn_wc__db_info_t *mtb = apr_pcalloc(result_pool, sizeof(*mtb));
const svn_checksum_t *checksum;
SVN_ERR(svn_wc__db_read_info(&mtb->status, &mtb->kind,
&mtb->revnum, &mtb->repos_relpath,
&mtb->repos_root_url, &mtb->repos_uuid,
&mtb->changed_rev, &mtb->changed_date,
&mtb->changed_author, &mtb->depth,
&checksum, NULL, NULL, NULL, NULL,
NULL, &mtb->lock, &mtb->recorded_size,
&mtb->recorded_mod_time, &mtb->changelist,
&mtb->conflicted, &mtb->op_root,
&mtb->had_props, &mtb->props_mod,
&mtb->have_base, &mtb->have_more_work, NULL,
db, local_abspath,
result_pool, scratch_pool));
SVN_ERR(svn_wc__db_wclocked(&mtb->locked, db, local_abspath, scratch_pool));
if (mtb->have_base
&& (mtb->status == svn_wc__db_status_added
|| mtb->status == svn_wc__db_status_deleted))
{
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
&mtb->lock, NULL, NULL,
db, local_abspath,
result_pool, scratch_pool));
}
mtb->has_checksum = (checksum != NULL);
#ifdef HAVE_SYMLINK
if (mtb->kind == svn_wc__db_kind_file
&& (mtb->had_props || mtb->props_mod))
{
apr_hash_t *properties;
if (mtb->props_mod)
SVN_ERR(svn_wc__db_read_props(&properties, db, local_abspath,
scratch_pool, scratch_pool));
else
SVN_ERR(svn_wc__db_read_pristine_props(&properties, db, local_abspath,
scratch_pool, scratch_pool));
mtb->special = (NULL != apr_hash_get(properties, SVN_PROP_SPECIAL,
APR_HASH_KEY_STRING));
}
#endif
*info = mtb;
return SVN_NO_ERROR;
}
static svn_error_t *
get_repos_root_url_relpath(const char **repos_relpath,
const char **repos_root_url,
const char **repos_uuid,
const struct svn_wc__db_info_t *info,
const char *parent_repos_relpath,
const char *parent_repos_root_url,
const char *parent_repos_uuid,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if (info->repos_relpath && info->repos_root_url)
{
*repos_relpath = info->repos_relpath;
*repos_root_url = info->repos_root_url;
*repos_uuid = info->repos_uuid;
}
else if (parent_repos_relpath && parent_repos_root_url)
{
*repos_relpath = svn_relpath_join(parent_repos_relpath,
svn_dirent_basename(local_abspath,
NULL),
result_pool);
*repos_root_url = parent_repos_root_url;
*repos_uuid = parent_repos_uuid;
}
else if (info->status == svn_wc__db_status_added)
{
SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
repos_relpath, repos_root_url,
repos_uuid, NULL, NULL, NULL, NULL,
db, local_abspath,
result_pool, scratch_pool));
}
else if (info->have_base)
{
SVN_ERR(svn_wc__db_scan_base_repos(repos_relpath, repos_root_url,
repos_uuid,
db, local_abspath,
result_pool, scratch_pool));
}
else
{
*repos_relpath = NULL;
*repos_root_url = NULL;
*repos_uuid = NULL;
}
return SVN_NO_ERROR;
}
static svn_error_t *
internal_status(svn_wc_status3_t **status,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
static svn_error_t *
assemble_status(svn_wc_status3_t **status,
svn_wc__db_t *db,
const char *local_abspath,
const char *parent_repos_root_url,
const char *parent_repos_relpath,
const char *parent_repos_uuid,
const struct svn_wc__db_info_t *info,
const svn_io_dirent2_t *dirent,
svn_boolean_t get_all,
svn_boolean_t ignore_text_mods,
const svn_lock_t *repos_lock,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc_status3_t *stat;
svn_boolean_t switched_p = FALSE;
svn_boolean_t copied = FALSE;
svn_boolean_t conflicted;
svn_error_t *err;
const char *repos_relpath;
const char *repos_root_url;
const char *repos_uuid;
svn_filesize_t filesize = (dirent && (dirent->kind == svn_node_file))
? dirent->filesize
: SVN_INVALID_FILESIZE;
enum svn_wc_status_kind node_status = svn_wc_status_normal;
enum svn_wc_status_kind text_status = svn_wc_status_normal;
enum svn_wc_status_kind prop_status = svn_wc_status_none;
if (!info)
SVN_ERR(read_info(&info, local_abspath, db, result_pool, scratch_pool));
if (!info->repos_relpath || !parent_repos_relpath)
switched_p = FALSE;
else
{
const char *name = svn_relpath_skip_ancestor(parent_repos_relpath,
info->repos_relpath);
switched_p = !name || (strcmp(name, svn_dirent_basename(local_abspath, NULL)) != 0);
}
if (info->kind == svn_wc__db_kind_dir)
{
if (info->status == svn_wc__db_status_incomplete || info->incomplete)
{
node_status = svn_wc_status_incomplete;
}
else if (info->status == svn_wc__db_status_deleted)
{
node_status = svn_wc_status_deleted;
if (!info->have_base)
copied = TRUE;
else
SVN_ERR(svn_wc__internal_node_get_schedule(NULL, &copied,
db, local_abspath,
scratch_pool));
}
else if (!dirent || dirent->kind != svn_node_dir)
{
if (!dirent || dirent->kind == svn_node_none)
node_status = svn_wc_status_missing;
else
node_status = svn_wc_status_obstructed;
}
}
else
{
if (info->status == svn_wc__db_status_deleted)
{
node_status = svn_wc_status_deleted;
SVN_ERR(svn_wc__internal_node_get_schedule(NULL, &copied,
db, local_abspath,
scratch_pool));
}
else if (!dirent || dirent->kind != svn_node_file)
{
if (!dirent || dirent->kind == svn_node_none)
node_status = svn_wc_status_missing;
else
node_status = svn_wc_status_obstructed;
}
}
if (info->status != svn_wc__db_status_deleted)
{
if (info->props_mod)
prop_status = svn_wc_status_modified;
else if (info->had_props)
prop_status = svn_wc_status_normal;
}
if (info->kind != svn_wc__db_kind_dir
&& node_status == svn_wc_status_normal)
{
svn_boolean_t text_modified_p = FALSE;
if ((info->kind == svn_wc__db_kind_file
|| info->kind == svn_wc__db_kind_symlink)
#ifdef HAVE_SYMLINK
&& (info->special == (dirent && dirent->special))
#endif
)
{
if (!info->has_checksum)
text_modified_p = TRUE;
else if (ignore_text_mods
||(dirent
&& info->recorded_size != SVN_INVALID_FILESIZE
&& info->recorded_mod_time != 0
&& info->recorded_size == dirent->filesize
&& info->recorded_mod_time == dirent->mtime))
text_modified_p = FALSE;
else
{
err = svn_wc__internal_file_modified_p(&text_modified_p,
db, local_abspath,
FALSE, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_ACCESS_DENIED)
return svn_error_trace(err);
svn_error_clear(err);
text_modified_p = TRUE;
}
}
}
#ifdef HAVE_SYMLINK
else if (info->special != (dirent && dirent->special))
node_status = svn_wc_status_obstructed;
#endif
if (text_modified_p)
text_status = svn_wc_status_modified;
}
conflicted = info->conflicted;
if (conflicted)
{
svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
SVN_ERR(svn_wc__internal_conflicted_p(&text_conflicted, &prop_conflicted,
&tree_conflicted,
db, local_abspath, scratch_pool));
if (!text_conflicted && !prop_conflicted && !tree_conflicted)
conflicted = FALSE;
}
if (node_status == svn_wc_status_normal)
{
if (info->status == svn_wc__db_status_added)
{
if (!info->op_root)
copied = TRUE;
else if (info->kind == svn_wc__db_kind_file
&& !info->have_base && !info->have_more_work)
{
node_status = svn_wc_status_added;
copied = info->has_checksum;
}
else
{
svn_wc_schedule_t schedule;
SVN_ERR(svn_wc__internal_node_get_schedule(&schedule, &copied,
db, local_abspath,
scratch_pool));
if (schedule == svn_wc_schedule_add)
node_status = svn_wc_status_added;
else if (schedule == svn_wc_schedule_replace)
node_status = svn_wc_status_replaced;
}
}
}
if (node_status == svn_wc_status_normal)
node_status = text_status;
if (node_status == svn_wc_status_normal
&& prop_status != svn_wc_status_none)
node_status = prop_status;
if (! get_all)
if (((node_status == svn_wc_status_none)
|| (node_status == svn_wc_status_normal))
&& (! switched_p)
&& (! info->locked )
&& (! info->lock)
&& (! repos_lock)
&& (! info->changelist)
&& (! conflicted))
{
*status = NULL;
return SVN_NO_ERROR;
}
SVN_ERR(get_repos_root_url_relpath(&repos_relpath, &repos_root_url,
&repos_uuid, info,
parent_repos_relpath,
parent_repos_root_url,
parent_repos_uuid,
db, local_abspath,
scratch_pool, scratch_pool));
stat = apr_pcalloc(result_pool, sizeof(**status));
switch (info->kind)
{
case svn_wc__db_kind_dir:
stat->kind = svn_node_dir;
break;
case svn_wc__db_kind_file:
case svn_wc__db_kind_symlink:
stat->kind = svn_node_file;
break;
case svn_wc__db_kind_unknown:
default:
stat->kind = svn_node_unknown;
}
stat->depth = info->depth;
stat->filesize = filesize;
stat->node_status = node_status;
stat->text_status = text_status;
stat->prop_status = prop_status;
stat->repos_node_status = svn_wc_status_none;
stat->repos_text_status = svn_wc_status_none;
stat->repos_prop_status = svn_wc_status_none;
stat->switched = switched_p;
stat->copied = copied;
stat->repos_lock = repos_lock;
stat->revision = info->revnum;
stat->changed_rev = info->changed_rev;
stat->changed_author = info->changed_author;
stat->changed_date = info->changed_date;
stat->ood_kind = svn_node_none;
stat->ood_changed_rev = SVN_INVALID_REVNUM;
stat->ood_changed_date = 0;
stat->ood_changed_author = NULL;
if (info->lock)
{
svn_lock_t *lck = apr_pcalloc(result_pool, sizeof(*lck));
lck->path = repos_relpath;
lck->token = info->lock->token;
lck->owner = info->lock->owner;
lck->comment = info->lock->comment;
lck->creation_date = info->lock->date;
stat->lock = lck;
}
else
stat->lock = NULL;
stat->locked = info->locked;
stat->conflicted = conflicted;
stat->versioned = TRUE;
stat->changelist = info->changelist;
stat->repos_root_url = repos_root_url;
stat->repos_relpath = repos_relpath;
stat->repos_uuid = repos_uuid;
*status = stat;
return SVN_NO_ERROR;
}
static svn_error_t *
assemble_unversioned(svn_wc_status3_t **status,
svn_wc__db_t *db,
const char *local_abspath,
const svn_io_dirent2_t *dirent,
svn_boolean_t tree_conflicted,
svn_boolean_t is_ignored,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc_status3_t *stat;
stat = apr_pcalloc(result_pool, sizeof(*stat));
stat->kind = svn_node_unknown;
stat->depth = svn_depth_unknown;
stat->filesize = (dirent && dirent->kind == svn_node_file)
? dirent->filesize
: SVN_INVALID_FILESIZE;
stat->node_status = svn_wc_status_none;
stat->text_status = svn_wc_status_none;
stat->prop_status = svn_wc_status_none;
stat->repos_node_status = svn_wc_status_none;
stat->repos_text_status = svn_wc_status_none;
stat->repos_prop_status = svn_wc_status_none;
if (dirent && dirent->kind != svn_node_none)
{
if (is_ignored)
stat->node_status = svn_wc_status_ignored;
else
stat->node_status = svn_wc_status_unversioned;
}
else if (tree_conflicted)
{
stat->node_status = svn_wc_status_conflicted;
}
stat->revision = SVN_INVALID_REVNUM;
stat->changed_rev = SVN_INVALID_REVNUM;
stat->ood_changed_rev = SVN_INVALID_REVNUM;
stat->ood_kind = svn_node_none;
stat->conflicted = tree_conflicted;
stat->changelist = NULL;
*status = stat;
return SVN_NO_ERROR;
}
static svn_error_t *
send_status_structure(const struct walk_status_baton *wb,
const char *local_abspath,
const char *parent_repos_root_url,
const char *parent_repos_relpath,
const char *parent_repos_uuid,
const struct svn_wc__db_info_t *info,
const svn_io_dirent2_t *dirent,
svn_boolean_t get_all,
svn_wc_status_func4_t status_func,
void *status_baton,
apr_pool_t *scratch_pool)
{
svn_wc_status3_t *statstruct;
const svn_lock_t *repos_lock = NULL;
if (wb->repos_locks)
{
const char *repos_relpath, *repos_root_url, *repos_uuid;
SVN_ERR(get_repos_root_url_relpath(&repos_relpath, &repos_root_url,
&repos_uuid,
info, parent_repos_relpath,
parent_repos_root_url,
parent_repos_uuid,
wb->db, local_abspath,
scratch_pool, scratch_pool));
if (repos_relpath)
{
repos_lock = apr_hash_get(wb->repos_locks,
svn_fspath__join("/", repos_relpath,
scratch_pool),
APR_HASH_KEY_STRING);
}
}
SVN_ERR(assemble_status(&statstruct, wb->db, local_abspath,
parent_repos_root_url, parent_repos_relpath,
parent_repos_uuid,
info, dirent, get_all, wb->ignore_text_mods,
repos_lock, scratch_pool, scratch_pool));
if (statstruct && status_func)
return svn_error_trace((*status_func)(status_baton, local_abspath,
statstruct, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
collect_ignore_patterns(apr_array_header_t **patterns,
svn_wc__db_t *db,
const char *local_abspath,
const apr_array_header_t *ignores,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
const svn_string_t *value;
*patterns = apr_array_make(result_pool, 1, sizeof(const char *));
for (i = 0; i < ignores->nelts; i++)
{
const char *ignore = APR_ARRAY_IDX(ignores, i, const char *);
APR_ARRAY_PUSH(*patterns, const char *) = apr_pstrdup(result_pool,
ignore);
}
SVN_ERR(svn_wc__internal_propget(&value, db, local_abspath, SVN_PROP_IGNORE,
scratch_pool, scratch_pool));
if (value != NULL)
svn_cstring_split_append(*patterns, value->data, "\n\r", FALSE,
result_pool);
return SVN_NO_ERROR;
}
static svn_boolean_t
is_external_path(apr_hash_t *externals,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
if (apr_hash_get(externals, local_abspath, APR_HASH_KEY_STRING))
return TRUE;
for (hi = apr_hash_first(scratch_pool, externals);
hi;
hi = apr_hash_next(hi))
{
const char *external_abspath = svn__apr_hash_index_key(hi);
if (svn_dirent_is_child(local_abspath, external_abspath, NULL))
return TRUE;
}
return FALSE;
}
static svn_error_t *
send_unversioned_item(const struct walk_status_baton *wb,
const char *local_abspath,
const svn_io_dirent2_t *dirent,
svn_boolean_t tree_conflicted,
const apr_array_header_t *patterns,
svn_boolean_t no_ignore,
svn_wc_status_func4_t status_func,
void *status_baton,
apr_pool_t *scratch_pool)
{
svn_boolean_t is_ignored;
svn_boolean_t is_external;
svn_wc_status3_t *status;
is_ignored = svn_wc_match_ignore_list(
svn_dirent_basename(local_abspath, NULL),
patterns, scratch_pool);
SVN_ERR(assemble_unversioned(&status,
wb->db, local_abspath,
dirent, tree_conflicted, is_ignored,
scratch_pool, scratch_pool));
is_external = is_external_path(wb->externals, local_abspath, scratch_pool);
if (is_external)
status->node_status = svn_wc_status_external;
if (status->conflicted)
is_ignored = FALSE;
if (no_ignore || (! is_ignored) || is_external)
return svn_error_trace((*status_func)(status_baton, local_abspath,
status, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
get_dir_status(const struct walk_status_baton *wb,
const char *local_abspath,
const char *selected,
svn_boolean_t skip_this_dir,
const char *parent_repos_root_url,
const char *parent_repos_relpath,
const char *parent_repos_uuid,
const struct svn_wc__db_info_t *dir_info,
const svn_io_dirent2_t *dirent,
const apr_array_header_t *ignore_patterns,
svn_depth_t depth,
svn_boolean_t get_all,
svn_boolean_t no_ignore,
svn_wc_status_func4_t status_func,
void *status_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
const char *dir_repos_root_url;
const char *dir_repos_relpath;
const char *dir_repos_uuid;
apr_hash_t *dirents, *nodes, *conflicts, *all_children;
apr_array_header_t *patterns = NULL;
apr_array_header_t *sorted_children;
apr_pool_t *iterpool, *subpool = svn_pool_create(scratch_pool);
svn_error_t *err;
int i;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
if (depth == svn_depth_unknown)
depth = svn_depth_infinity;
iterpool = svn_pool_create(subpool);
err = svn_io_get_dirents3(&dirents, local_abspath, FALSE, subpool, iterpool);
if (err
&& (APR_STATUS_IS_ENOENT(err->apr_err)
|| SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
{
svn_error_clear(err);
dirents = apr_hash_make(subpool);
}
else
SVN_ERR(err);
if (!dir_info)
SVN_ERR(read_info(&dir_info, local_abspath, wb->db,
subpool, iterpool));
SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url,
&dir_repos_uuid, dir_info,
parent_repos_relpath,
parent_repos_root_url, parent_repos_uuid,
wb->db, local_abspath,
subpool, iterpool));
if (selected == NULL)
{
SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
wb->db, local_abspath,
subpool, iterpool));
all_children = apr_hash_overlay(subpool, nodes, dirents);
if (apr_hash_count(conflicts) > 0)
all_children = apr_hash_overlay(subpool, conflicts, all_children);
}
else
{
const struct svn_wc__db_info_t *info;
const char *selected_abspath = svn_dirent_join(local_abspath, selected,
iterpool);
all_children = apr_hash_make(subpool);
nodes = apr_hash_make(subpool);
conflicts = apr_hash_make(subpool);
err = read_info(&info, selected_abspath, wb->db, subpool, iterpool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
}
else
{
if (!info->conflicted
|| info->status != svn_wc__db_status_normal
|| info->kind != svn_wc__db_kind_unknown)
{
apr_hash_set(nodes, selected, APR_HASH_KEY_STRING, info);
}
if (info->conflicted)
apr_hash_set(conflicts, selected, APR_HASH_KEY_STRING, info);
}
apr_hash_set(all_children, selected, APR_HASH_KEY_STRING, selected);
}
if (!selected)
{
if (! skip_this_dir)
SVN_ERR(send_status_structure(wb, local_abspath,
parent_repos_root_url,
parent_repos_relpath,
parent_repos_uuid,
dir_info, dirent, get_all,
status_func, status_baton,
iterpool));
if (depth == svn_depth_empty)
return SVN_NO_ERROR;
}
sorted_children = svn_sort__hash(all_children,
svn_sort_compare_items_lexically,
subpool);
for (i = 0; i < sorted_children->nelts; i++)
{
const void *key;
apr_ssize_t klen;
const char *node_abspath;
svn_io_dirent2_t *dirent_p;
const struct svn_wc__db_info_t *info;
svn_sort__item_t item;
svn_pool_clear(iterpool);
item = APR_ARRAY_IDX(sorted_children, i, svn_sort__item_t);
key = item.key;
klen = item.klen;
node_abspath = svn_dirent_join(local_abspath, key, iterpool);
dirent_p = apr_hash_get(dirents, key, klen);
info = apr_hash_get(nodes, key, klen);
if (info)
{
if (info->status != svn_wc__db_status_not_present
&& info->status != svn_wc__db_status_excluded
&& info->status != svn_wc__db_status_server_excluded)
{
if (depth == svn_depth_files
&& info->kind == svn_wc__db_kind_dir)
{
continue;
}
SVN_ERR(send_status_structure(wb, node_abspath,
dir_repos_root_url,
dir_repos_relpath,
dir_repos_uuid,
info, dirent_p, get_all,
status_func, status_baton,
iterpool));
if (depth == svn_depth_infinity
&& info->kind == svn_wc__db_kind_dir)
{
SVN_ERR(get_dir_status(wb, node_abspath, NULL, TRUE,
dir_repos_root_url, dir_repos_relpath,
dir_repos_uuid, info,
dirent_p, ignore_patterns,
svn_depth_infinity, get_all,
no_ignore,
status_func, status_baton,
cancel_func, cancel_baton,
iterpool));
}
continue;
}
}
if (apr_hash_get(conflicts, key, klen))
{
if (ignore_patterns && ! patterns)
SVN_ERR(collect_ignore_patterns(&patterns, wb->db, local_abspath,
ignore_patterns, subpool,
iterpool));
SVN_ERR(send_unversioned_item(wb,
node_abspath,
dirent_p, TRUE,
patterns,
no_ignore,
status_func,
status_baton,
iterpool));
continue;
}
if (dirent_p == NULL)
continue;
if (depth == svn_depth_files && dirent_p->kind == svn_node_dir)
continue;
if (svn_wc_is_adm_dir(key, iterpool))
continue;
if (ignore_patterns && ! patterns)
SVN_ERR(collect_ignore_patterns(&patterns, wb->db, local_abspath,
ignore_patterns, subpool,
iterpool));
SVN_ERR(send_unversioned_item(wb,
node_abspath,
dirent_p, FALSE,
patterns,
no_ignore || selected,
status_func, status_baton,
iterpool));
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
hash_stash(void *baton,
const char *path,
const svn_wc_status3_t *status,
apr_pool_t *scratch_pool)
{
apr_hash_t *stat_hash = baton;
apr_pool_t *hash_pool = apr_hash_pool_get(stat_hash);
assert(! apr_hash_get(stat_hash, path, APR_HASH_KEY_STRING));
apr_hash_set(stat_hash, apr_pstrdup(hash_pool, path),
APR_HASH_KEY_STRING, svn_wc_dup_status3(status, hash_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
tweak_statushash(void *baton,
void *this_dir_baton,
svn_boolean_t is_dir_baton,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t is_dir,
enum svn_wc_status_kind repos_node_status,
enum svn_wc_status_kind repos_text_status,
enum svn_wc_status_kind repos_prop_status,
svn_revnum_t deleted_rev,
const svn_lock_t *repos_lock,
apr_pool_t *scratch_pool)
{
svn_wc_status3_t *statstruct;
apr_pool_t *pool;
apr_hash_t *statushash;
if (is_dir_baton)
statushash = ((struct dir_baton *) baton)->statii;
else
statushash = ((struct file_baton *) baton)->dir_baton->statii;
pool = apr_hash_pool_get(statushash);
statstruct = apr_hash_get(statushash, local_abspath, APR_HASH_KEY_STRING);
if (! statstruct)
{
if (repos_node_status != svn_wc_status_added)
return SVN_NO_ERROR;
SVN_ERR(internal_status(&statstruct, db, local_abspath, pool,
scratch_pool));
statstruct->repos_lock = repos_lock;
apr_hash_set(statushash, apr_pstrdup(pool, local_abspath),
APR_HASH_KEY_STRING, statstruct);
}
if ((repos_node_status == svn_wc_status_added)
&& (statstruct->repos_node_status == svn_wc_status_deleted))
repos_node_status = svn_wc_status_replaced;
if (repos_node_status)
statstruct->repos_node_status = repos_node_status;
if (repos_text_status)
statstruct->repos_text_status = repos_text_status;
if (repos_prop_status)
statstruct->repos_prop_status = repos_prop_status;
if (is_dir_baton)
{
struct dir_baton *b = this_dir_baton;
if (!statstruct->repos_relpath && b->repos_relpath)
{
if (statstruct->repos_node_status == svn_wc_status_deleted)
{
statstruct->repos_relpath =
svn_relpath_join(b->repos_relpath,
svn_dirent_basename(local_abspath,
NULL),
pool);
}
else
statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath);
statstruct->repos_root_url =
b->edit_baton->anchor_status->repos_root_url;
statstruct->repos_uuid =
b->edit_baton->anchor_status->repos_uuid;
}
if (statstruct->repos_node_status == svn_wc_status_deleted)
{
statstruct->ood_kind = is_dir ? svn_node_dir : svn_node_file;
if (deleted_rev == SVN_INVALID_REVNUM)
statstruct->ood_changed_rev =
((struct dir_baton *) baton)->ood_changed_rev;
else
statstruct->ood_changed_rev = deleted_rev;
}
else
{
statstruct->ood_kind = b->ood_kind;
statstruct->ood_changed_rev = b->ood_changed_rev;
statstruct->ood_changed_date = b->ood_changed_date;
if (b->ood_changed_author)
statstruct->ood_changed_author =
apr_pstrdup(pool, b->ood_changed_author);
}
}
else
{
struct file_baton *b = baton;
statstruct->ood_changed_rev = b->ood_changed_rev;
statstruct->ood_changed_date = b->ood_changed_date;
if (!statstruct->repos_relpath && b->repos_relpath)
{
statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath);
statstruct->repos_root_url =
b->edit_baton->anchor_status->repos_root_url;
statstruct->repos_uuid =
b->edit_baton->anchor_status->repos_uuid;
}
statstruct->ood_kind = b->ood_kind;
if (b->ood_changed_author)
statstruct->ood_changed_author =
apr_pstrdup(pool, b->ood_changed_author);
}
return SVN_NO_ERROR;
}
static const char *
find_dir_repos_relpath(const struct dir_baton *db, apr_pool_t *pool)
{
if (! db->name)
return db->edit_baton->anchor_status->repos_relpath;
else
{
const char *repos_relpath;
struct dir_baton *pb = db->parent_baton;
const svn_wc_status3_t *status = apr_hash_get(pb->statii,
db->local_abspath,
APR_HASH_KEY_STRING);
if (status && status->repos_relpath)
return status->repos_relpath;
repos_relpath = find_dir_repos_relpath(pb, pool);
return svn_relpath_join(repos_relpath, db->name, pool);
}
}
static svn_error_t *
make_dir_baton(void **dir_baton,
const char *path,
struct edit_baton *edit_baton,
struct dir_baton *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = edit_baton;
struct dir_baton *d = apr_pcalloc(pool, sizeof(*d));
const char *local_abspath;
const svn_wc_status3_t *status_in_parent;
SVN_ERR_ASSERT(path || (! pb));
if (pb)
local_abspath = svn_dirent_join(eb->anchor_abspath, path, pool);
else
local_abspath = eb->anchor_abspath;
d->local_abspath = local_abspath;
d->name = path ? svn_dirent_basename(path, pool) : NULL;
d->edit_baton = edit_baton;
d->parent_baton = parent_baton;
d->pool = pool;
d->statii = apr_hash_make(pool);
d->ood_changed_rev = SVN_INVALID_REVNUM;
d->ood_changed_date = 0;
d->repos_relpath = apr_pstrdup(pool, find_dir_repos_relpath(d, pool));
d->ood_kind = svn_node_dir;
d->ood_changed_author = NULL;
if (pb)
{
if (pb->excluded)
d->excluded = TRUE;
else if (pb->depth == svn_depth_immediates)
d->depth = svn_depth_empty;
else if (pb->depth == svn_depth_files || pb->depth == svn_depth_empty)
d->excluded = TRUE;
else if (pb->depth == svn_depth_unknown)
d->depth = svn_depth_unknown;
else
d->depth = svn_depth_infinity;
}
else
{
d->depth = eb->default_depth;
}
if (pb)
status_in_parent = apr_hash_get(pb->statii, d->local_abspath,
APR_HASH_KEY_STRING);
else
status_in_parent = eb->anchor_status;
if (status_in_parent
&& status_in_parent->versioned
&& (status_in_parent->kind == svn_node_dir)
&& (! d->excluded)
&& (d->depth == svn_depth_unknown
|| d->depth == svn_depth_infinity
|| d->depth == svn_depth_files
|| d->depth == svn_depth_immediates)
)
{
const svn_wc_status3_t *this_dir_status;
const apr_array_header_t *ignores = eb->ignores;
SVN_ERR(get_dir_status(&eb->wb, local_abspath, NULL, TRUE,
status_in_parent->repos_root_url,
NULL ,
status_in_parent->repos_uuid,
NULL,
NULL , ignores,
d->depth == svn_depth_files
? svn_depth_files
: svn_depth_immediates,
TRUE, TRUE,
hash_stash, d->statii,
eb->cancel_func, eb->cancel_baton,
pool));
this_dir_status = apr_hash_get(d->statii, d->local_abspath,
APR_HASH_KEY_STRING);
if (this_dir_status && this_dir_status->versioned
&& (d->depth == svn_depth_unknown
|| d->depth > status_in_parent->depth))
{
d->depth = this_dir_status->depth;
}
}
*dir_baton = d;
return SVN_NO_ERROR;
}
static struct file_baton *
make_file_baton(struct dir_baton *parent_dir_baton,
const char *path,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_dir_baton;
struct edit_baton *eb = pb->edit_baton;
struct file_baton *f = apr_pcalloc(pool, sizeof(*f));
f->local_abspath = svn_dirent_join(eb->anchor_abspath, path, pool);
f->name = svn_dirent_basename(f->local_abspath, NULL);
f->pool = pool;
f->dir_baton = pb;
f->edit_baton = eb;
f->ood_changed_rev = SVN_INVALID_REVNUM;
f->ood_changed_date = 0;
f->repos_relpath = svn_relpath_join(find_dir_repos_relpath(pb, pool),
f->name, pool);
f->ood_kind = svn_node_file;
f->ood_changed_author = NULL;
return f;
}
static svn_boolean_t
is_sendable_status(const svn_wc_status3_t *status,
svn_boolean_t no_ignore,
svn_boolean_t get_all)
{
if (status->repos_node_status != svn_wc_status_none)
return TRUE;
if (status->repos_lock)
return TRUE;
if (status->conflicted)
return TRUE;
if ((status->node_status == svn_wc_status_ignored) && (! no_ignore))
return FALSE;
if (get_all)
return TRUE;
if (status->node_status == svn_wc_status_unversioned)
return TRUE;
if ((status->node_status != svn_wc_status_none
&& (status->node_status != svn_wc_status_normal)))
return TRUE;
if (status->switched)
return TRUE;
if (status->versioned && status->lock)
return TRUE;
if (status->changelist)
return TRUE;
return FALSE;
}
struct status_baton
{
svn_wc_status_func4_t real_status_func;
void *real_status_baton;
};
static svn_error_t *
mark_deleted(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *scratch_pool)
{
struct status_baton *sb = baton;
svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool);
new_status->repos_node_status = svn_wc_status_deleted;
return sb->real_status_func(sb->real_status_baton, local_abspath,
new_status, scratch_pool);
}
static svn_error_t *
handle_statii(struct edit_baton *eb,
const char *dir_repos_root_url,
const char *dir_repos_relpath,
const char *dir_repos_uuid,
apr_hash_t *statii,
svn_boolean_t dir_was_deleted,
svn_depth_t depth,
apr_pool_t *pool)
{
const apr_array_header_t *ignores = eb->ignores;
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(pool);
svn_wc_status_func4_t status_func = eb->status_func;
void *status_baton = eb->status_baton;
struct status_baton sb;
if (dir_was_deleted)
{
sb.real_status_func = eb->status_func;
sb.real_status_baton = eb->status_baton;
status_func = mark_deleted;
status_baton = &sb;
}
for (hi = apr_hash_first(pool, statii); hi; hi = apr_hash_next(hi))
{
const char *local_abspath = svn__apr_hash_index_key(hi);
svn_wc_status3_t *status = svn__apr_hash_index_val(hi);
svn_pool_clear(iterpool);
if (status->versioned && status->kind == svn_node_dir
&& (depth == svn_depth_unknown
|| depth == svn_depth_infinity))
{
SVN_ERR(get_dir_status(&eb->wb,
local_abspath, NULL, TRUE,
dir_repos_root_url, dir_repos_relpath,
dir_repos_uuid,
NULL,
NULL ,
ignores, depth, eb->get_all, eb->no_ignore,
status_func, status_baton,
eb->cancel_func, eb->cancel_baton,
iterpool));
}
if (dir_was_deleted)
status->repos_node_status = svn_wc_status_deleted;
if (is_sendable_status(status, eb->no_ignore, eb->get_all))
SVN_ERR((eb->status_func)(eb->status_baton, local_abspath, status,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
set_target_revision(void *edit_baton,
svn_revnum_t target_revision,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
*(eb->target_revision) = target_revision;
return SVN_NO_ERROR;
}
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **dir_baton)
{
struct edit_baton *eb = edit_baton;
eb->root_opened = TRUE;
return make_dir_baton(dir_baton, NULL, eb, NULL, pool);
}
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t revision,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *db = parent_baton;
struct edit_baton *eb = db->edit_baton;
const char *local_abspath = svn_dirent_join(eb->anchor_abspath, path, pool);
svn_wc__db_kind_t kind;
SVN_ERR(svn_wc__db_read_kind(&kind, eb->db, local_abspath, FALSE, pool));
SVN_ERR(tweak_statushash(db, db, TRUE, eb->db,
local_abspath, kind == svn_wc__db_kind_dir,
svn_wc_status_deleted, 0, 0, revision, NULL, pool));
if (db->parent_baton && (! *eb->target_basename))
SVN_ERR(tweak_statushash(db->parent_baton, db, TRUE,eb->db,
db->local_abspath,
kind == svn_wc__db_kind_dir,
svn_wc_status_modified, svn_wc_status_modified,
0, SVN_INVALID_REVNUM, NULL, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
add_directory(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
struct dir_baton *new_db;
SVN_ERR(make_dir_baton(child_baton, path, eb, pb, pool));
new_db = *child_baton;
new_db->added = TRUE;
pb->text_changed = TRUE;
return SVN_NO_ERROR;
}
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
return make_dir_baton(child_baton, path, pb->edit_baton, pb, pool);
}
static svn_error_t *
change_dir_prop(void *dir_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct dir_baton *db = dir_baton;
if (svn_wc_is_normal_prop(name))
db->prop_changed = TRUE;
if (value != NULL)
{
if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_REV) == 0)
db->ood_changed_rev = SVN_STR_TO_REV(value->data);
else if (strcmp(name, SVN_PROP_ENTRY_LAST_AUTHOR) == 0)
db->ood_changed_author = apr_pstrdup(db->pool, value->data);
else if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_DATE) == 0)
{
apr_time_t tm;
SVN_ERR(svn_time_from_cstring(&tm, value->data, db->pool));
db->ood_changed_date = tm;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
struct dir_baton *db = dir_baton;
struct dir_baton *pb = db->parent_baton;
struct edit_baton *eb = db->edit_baton;
if (db->added || db->prop_changed || db->text_changed
|| db->ood_changed_rev != SVN_INVALID_REVNUM)
{
enum svn_wc_status_kind repos_node_status;
enum svn_wc_status_kind repos_text_status;
enum svn_wc_status_kind repos_prop_status;
if (db->added)
{
repos_node_status = svn_wc_status_added;
repos_text_status = svn_wc_status_added;
repos_prop_status = db->prop_changed ? svn_wc_status_added
: svn_wc_status_none;
}
else
{
repos_node_status = (db->text_changed || db->prop_changed)
? svn_wc_status_modified
: svn_wc_status_none;
repos_text_status = db->text_changed ? svn_wc_status_modified
: svn_wc_status_none;
repos_prop_status = db->prop_changed ? svn_wc_status_modified
: svn_wc_status_none;
}
if (pb)
{
SVN_ERR(tweak_statushash(pb, db, TRUE, eb->db, db->local_abspath,
TRUE, repos_node_status, repos_text_status,
repos_prop_status, SVN_INVALID_REVNUM, NULL,
pool));
}
else
{
eb->anchor_status->repos_node_status = repos_node_status;
eb->anchor_status->repos_prop_status = repos_prop_status;
eb->anchor_status->repos_text_status = repos_text_status;
if (db->ood_changed_rev != eb->anchor_status->revision)
{
eb->anchor_status->ood_changed_rev = db->ood_changed_rev;
eb->anchor_status->ood_changed_date = db->ood_changed_date;
eb->anchor_status->ood_kind = db->ood_kind;
eb->anchor_status->ood_changed_author =
apr_pstrdup(pool, db->ood_changed_author);
}
}
}
if (pb && ! db->excluded)
{
svn_boolean_t was_deleted = FALSE;
const svn_wc_status3_t *dir_status;
dir_status = apr_hash_get(pb->statii, db->local_abspath,
APR_HASH_KEY_STRING);
if (dir_status &&
((dir_status->repos_node_status == svn_wc_status_deleted)
|| (dir_status->repos_node_status == svn_wc_status_replaced)))
was_deleted = TRUE;
SVN_ERR(handle_statii(eb,
dir_status ? dir_status->repos_root_url : NULL,
dir_status ? dir_status->repos_relpath : NULL,
dir_status ? dir_status->repos_uuid : NULL,
db->statii, was_deleted, db->depth, pool));
if (dir_status && is_sendable_status(dir_status, eb->no_ignore,
eb->get_all))
SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath,
dir_status, pool));
apr_hash_set(pb->statii, db->local_abspath, APR_HASH_KEY_STRING, NULL);
}
else if (! pb)
{
if (*eb->target_basename)
{
const svn_wc_status3_t *tgt_status;
tgt_status = apr_hash_get(db->statii, eb->target_abspath,
APR_HASH_KEY_STRING);
if (tgt_status)
{
if (tgt_status->versioned
&& tgt_status->kind == svn_node_dir)
{
SVN_ERR(get_dir_status(&eb->wb,
eb->target_abspath, NULL, TRUE,
NULL, NULL, NULL, NULL,
NULL ,
eb->ignores,
eb->default_depth,
eb->get_all, eb->no_ignore,
eb->status_func, eb->status_baton,
eb->cancel_func, eb->cancel_baton,
pool));
}
if (is_sendable_status(tgt_status, eb->no_ignore, eb->get_all))
SVN_ERR((eb->status_func)(eb->status_baton, eb->target_abspath,
tgt_status, pool));
}
}
else
{
SVN_ERR(handle_statii(eb,
eb->anchor_status->repos_root_url,
eb->anchor_status->repos_relpath,
eb->anchor_status->repos_uuid,
db->statii, FALSE, eb->default_depth, pool));
if (is_sendable_status(eb->anchor_status, eb->no_ignore,
eb->get_all))
SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath,
eb->anchor_status, pool));
eb->anchor_status = NULL;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
add_file(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
apr_pool_t *pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct file_baton *new_fb = make_file_baton(pb, path, pool);
pb->text_changed = TRUE;
new_fb->added = TRUE;
*file_baton = new_fb;
return SVN_NO_ERROR;
}
static svn_error_t *
open_file(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct file_baton *new_fb = make_file_baton(pb, path, pool);
*file_baton = new_fb;
return SVN_NO_ERROR;
}
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_checksum,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
struct file_baton *fb = file_baton;
fb->text_changed = TRUE;
*handler_baton = NULL;
*handler = svn_delta_noop_window_handler;
return SVN_NO_ERROR;
}
static svn_error_t *
change_file_prop(void *file_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct file_baton *fb = file_baton;
if (svn_wc_is_normal_prop(name))
fb->prop_changed = TRUE;
if (value != NULL)
{
if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_REV) == 0)
fb->ood_changed_rev = SVN_STR_TO_REV(value->data);
else if (strcmp(name, SVN_PROP_ENTRY_LAST_AUTHOR) == 0)
fb->ood_changed_author = apr_pstrdup(fb->dir_baton->pool,
value->data);
else if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_DATE) == 0)
{
apr_time_t tm;
SVN_ERR(svn_time_from_cstring(&tm, value->data,
fb->dir_baton->pool));
fb->ood_changed_date = tm;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
close_file(void *file_baton,
const char *text_checksum,
apr_pool_t *pool)
{
struct file_baton *fb = file_baton;
enum svn_wc_status_kind repos_node_status;
enum svn_wc_status_kind repos_text_status;
enum svn_wc_status_kind repos_prop_status;
const svn_lock_t *repos_lock = NULL;
if (! (fb->added || fb->prop_changed || fb->text_changed))
return SVN_NO_ERROR;
if (fb->added)
{
repos_node_status = svn_wc_status_added;
repos_text_status = svn_wc_status_added;
repos_prop_status = fb->prop_changed ? svn_wc_status_added : 0;
if (fb->edit_baton->wb.repos_locks)
{
const char *dir_repos_relpath = find_dir_repos_relpath(fb->dir_baton,
pool);
const char *repos_relpath = svn_relpath_join(dir_repos_relpath,
fb->name, pool);
repos_lock = apr_hash_get(fb->edit_baton->wb.repos_locks,
svn_fspath__join("/", repos_relpath,
pool),
APR_HASH_KEY_STRING);
}
}
else
{
repos_node_status = (fb->text_changed || fb->prop_changed)
? svn_wc_status_modified : 0;
repos_text_status = fb->text_changed ? svn_wc_status_modified : 0;
repos_prop_status = fb->prop_changed ? svn_wc_status_modified : 0;
}
return tweak_statushash(fb, NULL, FALSE, fb->edit_baton->db,
fb->local_abspath, FALSE, repos_node_status,
repos_text_status, repos_prop_status,
SVN_INVALID_REVNUM, repos_lock, pool);
}
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
if (eb->root_opened)
return SVN_NO_ERROR;
SVN_ERR(svn_wc_walk_status(eb->wc_ctx,
eb->target_abspath,
eb->default_depth,
eb->get_all,
eb->no_ignore,
FALSE,
eb->ignores,
eb->status_func,
eb->status_baton,
eb->cancel_func,
eb->cancel_baton,
pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_get_status_editor5(const svn_delta_editor_t **editor,
void **edit_baton,
void **set_locks_baton,
svn_revnum_t *edit_revision,
svn_wc_context_t *wc_ctx,
const char *anchor_abspath,
const char *target_basename,
svn_depth_t depth,
svn_boolean_t get_all,
svn_boolean_t no_ignore,
svn_boolean_t depth_as_sticky,
svn_boolean_t server_performs_filtering,
const apr_array_header_t *ignore_patterns,
svn_wc_status_func4_t status_func,
void *status_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb;
svn_delta_editor_t *tree_editor = svn_delta_default_editor(result_pool);
void *inner_baton;
const svn_delta_editor_t *inner_editor;
eb = apr_pcalloc(result_pool, sizeof(*eb));
eb->default_depth = depth;
eb->target_revision = edit_revision;
eb->db = wc_ctx->db;
eb->wc_ctx = wc_ctx;
eb->get_all = get_all;
eb->no_ignore = no_ignore;
eb->status_func = status_func;
eb->status_baton = status_baton;
eb->cancel_func = cancel_func;
eb->cancel_baton = cancel_baton;
eb->anchor_abspath = apr_pstrdup(result_pool, anchor_abspath);
eb->target_abspath = svn_dirent_join(anchor_abspath, target_basename,
result_pool);
eb->target_basename = apr_pstrdup(result_pool, target_basename);
eb->root_opened = FALSE;
eb->wb.db = wc_ctx->db;
eb->wb.target_abspath = eb->target_abspath;
eb->wb.ignore_text_mods = FALSE;
eb->wb.repos_locks = NULL;
eb->wb.repos_root = NULL;
SVN_ERR(svn_wc__db_externals_defined_below(&eb->wb.externals,
wc_ctx->db, eb->target_abspath,
result_pool, scratch_pool));
if (ignore_patterns)
{
eb->ignores = ignore_patterns;
}
else
{
apr_array_header_t *ignores;
SVN_ERR(svn_wc_get_default_ignores(&ignores, NULL, result_pool));
eb->ignores = ignores;
}
SVN_ERR(internal_status(&(eb->anchor_status), wc_ctx->db, anchor_abspath,
result_pool, scratch_pool));
tree_editor->set_target_revision = set_target_revision;
tree_editor->open_root = open_root;
tree_editor->delete_entry = delete_entry;
tree_editor->add_directory = add_directory;
tree_editor->open_directory = open_directory;
tree_editor->change_dir_prop = change_dir_prop;
tree_editor->close_directory = close_directory;
tree_editor->add_file = add_file;
tree_editor->open_file = open_file;
tree_editor->apply_textdelta = apply_textdelta;
tree_editor->change_file_prop = change_file_prop;
tree_editor->close_file = close_file;
tree_editor->close_edit = close_edit;
inner_editor = tree_editor;
inner_baton = eb;
if (!server_performs_filtering
&& !depth_as_sticky)
SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
&inner_baton,
wc_ctx->db,
anchor_abspath,
target_basename,
inner_editor,
inner_baton,
result_pool));
SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
inner_editor, inner_baton,
editor, edit_baton,
result_pool));
if (set_locks_baton)
*set_locks_baton = eb;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__internal_walk_status(svn_wc__db_t *db,
const char *local_abspath,
svn_depth_t depth,
svn_boolean_t get_all,
svn_boolean_t no_ignore,
svn_boolean_t ignore_text_mods,
const apr_array_header_t *ignore_patterns,
svn_wc_status_func4_t status_func,
void *status_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
struct walk_status_baton wb;
const svn_io_dirent2_t *dirent;
const char *anchor_abspath, *target_name;
svn_boolean_t skip_root;
const struct svn_wc__db_info_t *dir_info;
svn_error_t *err;
wb.db = db;
wb.target_abspath = local_abspath;
wb.ignore_text_mods = ignore_text_mods;
wb.repos_root = NULL;
wb.repos_locks = NULL;
SVN_ERR(svn_wc__db_externals_defined_below(&wb.externals, db, local_abspath,
scratch_pool, scratch_pool));
if (!ignore_patterns)
{
apr_array_header_t *ignores;
SVN_ERR(svn_wc_get_default_ignores(&ignores, NULL, scratch_pool));
ignore_patterns = ignores;
}
err = read_info(&dir_info, local_abspath, db, scratch_pool, scratch_pool);
if (!err
&& dir_info->kind == svn_wc__db_kind_dir
&& dir_info->status != svn_wc__db_status_not_present
&& dir_info->status != svn_wc__db_status_excluded
&& dir_info->status != svn_wc__db_status_server_excluded)
{
anchor_abspath = local_abspath;
target_name = NULL;
skip_root = FALSE;
}
else if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
else
{
svn_error_clear(err);
dir_info = NULL;
anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
target_name = svn_dirent_basename(local_abspath, NULL);
skip_root = TRUE;
}
SVN_ERR(svn_io_stat_dirent(&dirent, local_abspath, TRUE,
scratch_pool, scratch_pool));
#ifdef HAVE_SYMLINK
if (dirent->special && !skip_root)
{
svn_io_dirent2_t *this_dirent = svn_io_dirent2_dup(dirent,
scratch_pool);
SVN_ERR(svn_io_check_resolved_path(local_abspath,
&this_dirent->kind, scratch_pool));
this_dirent->special = FALSE;
SVN_ERR(send_status_structure(&wb, local_abspath,
NULL, NULL, NULL,
dir_info, this_dirent, get_all,
status_func, status_baton,
scratch_pool));
skip_root = TRUE;
}
#endif
SVN_ERR(get_dir_status(&wb,
anchor_abspath,
target_name,
skip_root,
NULL, NULL, NULL,
dir_info,
dirent,
ignore_patterns,
depth,
get_all,
no_ignore,
status_func, status_baton,
cancel_func, cancel_baton,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_walk_status(svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_depth_t depth,
svn_boolean_t get_all,
svn_boolean_t no_ignore,
svn_boolean_t ignore_text_mods,
const apr_array_header_t *ignore_patterns,
svn_wc_status_func4_t status_func,
void *status_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
svn_wc__internal_walk_status(wc_ctx->db,
local_abspath,
depth,
get_all,
no_ignore,
ignore_text_mods,
ignore_patterns,
status_func,
status_baton,
cancel_func,
cancel_baton,
scratch_pool));
}
svn_error_t *
svn_wc_status_set_repos_locks(void *edit_baton,
apr_hash_t *locks,
const char *repos_root,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
eb->wb.repos_locks = locks;
eb->wb.repos_root = apr_pstrdup(pool, repos_root);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_get_default_ignores(apr_array_header_t **patterns,
apr_hash_t *config,
apr_pool_t *pool)
{
svn_config_t *cfg = config ? apr_hash_get(config,
SVN_CONFIG_CATEGORY_CONFIG,
APR_HASH_KEY_STRING) : NULL;
const char *val;
svn_config_get(cfg, &val, SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_GLOBAL_IGNORES,
SVN_CONFIG_DEFAULT_GLOBAL_IGNORES);
*patterns = apr_array_make(pool, 16, sizeof(const char *));
svn_cstring_split_append(*patterns, val, "\n\r\t\v ", FALSE, pool);
return SVN_NO_ERROR;
}
static svn_error_t *
internal_status(svn_wc_status3_t **status,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_io_dirent2_t *dirent;
svn_wc__db_kind_t node_kind;
const char *parent_repos_relpath;
const char *parent_repos_root_url;
const char *parent_repos_uuid;
svn_wc__db_status_t node_status;
svn_boolean_t conflicted;
svn_boolean_t is_root = FALSE;
svn_error_t *err;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_io_stat_dirent(&dirent, local_abspath, TRUE,
scratch_pool, scratch_pool));
err = svn_wc__db_read_info(&node_status, &node_kind, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
NULL, NULL, NULL, NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
node_kind = svn_wc__db_kind_unknown;
conflicted = FALSE;
}
else if (err)
{
return svn_error_trace(err);
}
else if (node_status == svn_wc__db_status_not_present
|| node_status == svn_wc__db_status_server_excluded
|| node_status == svn_wc__db_status_excluded)
{
node_kind = svn_wc__db_kind_unknown;
}
if (node_kind == svn_wc__db_kind_unknown)
return svn_error_trace(assemble_unversioned(status,
db, local_abspath,
dirent, conflicted,
FALSE ,
result_pool, scratch_pool));
if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
is_root = TRUE;
else
SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool));
if (!is_root)
{
svn_wc__db_status_t parent_status;
const char *parent_abspath = svn_dirent_dirname(local_abspath,
scratch_pool);
err = svn_wc__db_read_info(&parent_status, NULL, NULL,
&parent_repos_relpath, &parent_repos_root_url,
&parent_repos_uuid, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
db, parent_abspath,
result_pool, scratch_pool);
if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
|| SVN_WC__ERR_IS_NOT_CURRENT_WC(err)))
{
svn_error_clear(err);
parent_repos_root_url = NULL;
parent_repos_relpath = NULL;
parent_repos_uuid = NULL;
}
else SVN_ERR(err);
}
else
{
parent_repos_root_url = NULL;
parent_repos_relpath = NULL;
parent_repos_uuid = NULL;
}
return svn_error_trace(assemble_status(status, db, local_abspath,
parent_repos_root_url,
parent_repos_relpath,
parent_repos_uuid,
NULL,
dirent,
TRUE ,
FALSE,
NULL ,
result_pool, scratch_pool));
}
svn_error_t *
svn_wc_status3(svn_wc_status3_t **status,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
internal_status(status, wc_ctx->db, local_abspath, result_pool,
scratch_pool));
}
svn_wc_status3_t *
svn_wc_dup_status3(const svn_wc_status3_t *orig_stat,
apr_pool_t *pool)
{
svn_wc_status3_t *new_stat = apr_palloc(pool, sizeof(*new_stat));
*new_stat = *orig_stat;
if (orig_stat->repos_lock)
new_stat->repos_lock = svn_lock_dup(orig_stat->repos_lock, pool);
if (orig_stat->changed_author)
new_stat->changed_author = apr_pstrdup(pool, orig_stat->changed_author);
if (orig_stat->ood_changed_author)
new_stat->ood_changed_author
= apr_pstrdup(pool, orig_stat->ood_changed_author);
if (orig_stat->lock)
new_stat->lock = svn_lock_dup(orig_stat->lock, pool);
if (orig_stat->changelist)
new_stat->changelist
= apr_pstrdup(pool, orig_stat->changelist);
if (orig_stat->repos_root_url)
new_stat->repos_root_url
= apr_pstrdup(pool, orig_stat->repos_root_url);
if (orig_stat->repos_relpath)
new_stat->repos_relpath
= apr_pstrdup(pool, orig_stat->repos_relpath);
if (orig_stat->repos_uuid)
new_stat->repos_uuid
= apr_pstrdup(pool, orig_stat->repos_uuid);
return new_stat;
}
svn_error_t *
svn_wc_get_ignores2(apr_array_header_t **patterns,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_hash_t *config,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *default_ignores;
SVN_ERR(svn_wc_get_default_ignores(&default_ignores, config, scratch_pool));
return svn_error_trace(collect_ignore_patterns(patterns, wc_ctx->db,
local_abspath,
default_ignores,
result_pool, scratch_pool));
}