#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_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 "private/svn_sorts_private.h"
#include "private/svn_wc_private.h"
#include "private/svn_fspath.h"
#include "private/svn_editor.h"
typedef struct svn_wc__internal_status_t
{
svn_wc_status3_t s;
svn_boolean_t has_descendants;
svn_boolean_t op_root;
} svn_wc__internal_status_t;
struct walk_status_baton
{
svn_wc__db_t *db;
const char *target_abspath;
svn_boolean_t ignore_text_mods;
svn_boolean_t check_working_copy;
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_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__internal_status_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 *
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 = apr_pstrdup(result_pool, info->repos_relpath);
*repos_root_url = apr_pstrdup(result_pool, info->repos_root_url);
*repos_uuid = apr_pstrdup(result_pool, 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 = apr_pstrdup(result_pool, parent_repos_root_url);
*repos_uuid = apr_pstrdup(result_pool, parent_repos_uuid);
}
else
{
SVN_ERR(svn_wc__db_read_repos_info(NULL,
repos_relpath, repos_root_url,
repos_uuid,
db, local_abspath,
result_pool, scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
internal_status(svn_wc__internal_status_t **status,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t check_working_copy,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
static svn_error_t *
assemble_status(svn_wc__internal_status_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,
svn_boolean_t check_working_copy,
const svn_lock_t *repos_lock,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__internal_status_t *inner_stat;
svn_wc_status3_t *stat;
svn_boolean_t switched_p = FALSE;
svn_boolean_t copied = FALSE;
svn_boolean_t conflicted;
const char *moved_from_abspath = NULL;
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->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->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 || info->have_more_work || info->copied)
copied = TRUE;
else if (!info->have_more_work && info->have_base)
copied = FALSE;
else
{
const char *work_del_abspath;
SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
&work_del_abspath, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
if (work_del_abspath)
copied = TRUE;
}
}
else if (check_working_copy)
{
svn_node_kind_t expected_kind = (info->kind == svn_node_dir)
? svn_node_dir
: svn_node_file;
if (!dirent || dirent->kind != expected_kind)
{
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_node_dir
&& node_status == svn_wc_status_normal)
{
svn_boolean_t text_modified_p = FALSE;
if ((info->kind == svn_node_file
|| info->kind == svn_node_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_time != 0
&& info->recorded_size == dirent->filesize
&& info->recorded_time == dirent->mtime))
text_modified_p = FALSE;
else
{
svn_error_t *err;
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)
{
copied = info->copied;
if (!info->op_root)
{ }
else if (!info->have_base && !info->have_more_work)
{
node_status = svn_wc_status_added;
}
else
{
svn_wc__db_status_t below_working;
svn_boolean_t have_base, have_work;
SVN_ERR(svn_wc__db_info_below_working(&have_base, &have_work,
&below_working,
db, local_abspath,
scratch_pool));
if (below_working != svn_wc__db_status_not_present
&& below_working != svn_wc__db_status_deleted)
{
node_status = svn_wc_status_replaced;
}
else
node_status = svn_wc_status_added;
}
if (info->moved_here && info->op_root)
{
svn_error_t *err;
err = svn_wc__db_scan_moved(&moved_from_abspath, NULL, NULL, NULL,
db, local_abspath,
result_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
return svn_error_trace(err);
svn_error_clear(err);
moved_from_abspath = NULL;
}
}
}
}
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)
|| (node_status == svn_wc_status_deleted && !info->op_root))
&& (! switched_p)
&& (! info->locked)
&& (! info->lock)
&& (! repos_lock)
&& (! info->changelist)
&& (! conflicted)
&& (! info->moved_to))
{
*status = NULL;
return SVN_NO_ERROR;
}
inner_stat = apr_pcalloc(result_pool, sizeof(*inner_stat));
stat = &inner_stat->s;
inner_stat->has_descendants = info->has_descendants;
inner_stat->op_root = info->op_root;
switch (info->kind)
{
case svn_node_dir:
stat->kind = svn_node_dir;
break;
case svn_node_file:
case svn_node_symlink:
stat->kind = svn_node_file;
break;
case svn_node_unknown:
default:
stat->kind = svn_node_unknown;
}
stat->depth = info->depth;
if (dirent)
{
stat->filesize = (dirent->kind == svn_node_file)
? dirent->filesize
: SVN_INVALID_FILESIZE;
stat->actual_kind = dirent->special ? svn_node_symlink
: dirent->kind;
}
else
{
stat->filesize = SVN_INVALID_FILESIZE;
stat->actual_kind = ignore_text_mods ? svn_node_unknown
: svn_node_none;
}
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;
if (info->changed_author)
stat->changed_author = apr_pstrdup(result_pool, 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;
SVN_ERR(get_repos_root_url_relpath(&stat->repos_relpath,
&stat->repos_root_url,
&stat->repos_uuid, info,
parent_repos_relpath,
parent_repos_root_url,
parent_repos_uuid,
db, local_abspath,
result_pool, scratch_pool));
if (info->lock)
{
svn_lock_t *lck = svn_lock_create(result_pool);
lck->path = stat->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;
if (info->changelist)
stat->changelist = apr_pstrdup(result_pool, info->changelist);
stat->moved_from_abspath = moved_from_abspath;
if (info->moved_to)
stat->moved_to_abspath = apr_pstrdup(result_pool,
info->moved_to->moved_to_abspath);
stat->file_external = info->file_external;
*status = inner_stat;
return SVN_NO_ERROR;
}
static svn_error_t *
assemble_unversioned(svn_wc__internal_status_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__internal_status_t *inner_status;
svn_wc_status3_t *stat;
inner_status = apr_pcalloc(result_pool, sizeof(*inner_status));
stat = &inner_status->s;
stat->kind = svn_node_unknown;
stat->depth = svn_depth_unknown;
if (dirent)
{
stat->actual_kind = dirent->special ? svn_node_symlink
: dirent->kind;
stat->filesize = (dirent->kind == svn_node_file)
? dirent->filesize
: SVN_INVALID_FILESIZE;
}
else
{
stat->actual_kind = svn_node_none;
stat->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 = inner_status;
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__internal_status_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 = svn_hash_gets(wb->repos_locks,
svn_fspath__join("/", repos_relpath,
scratch_pool));
}
}
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, wb->check_working_copy,
repos_lock, scratch_pool, scratch_pool));
if (statstruct && status_func)
return svn_error_trace((*status_func)(status_baton, local_abspath,
&statstruct->s,
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;
apr_hash_t *props;
apr_array_header_t *inherited_props;
svn_error_t *err;
*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);
}
err = svn_wc__db_read_inherited_props(&inherited_props, &props,
db, local_abspath,
SVN_PROP_INHERITABLE_IGNORES,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
return svn_error_trace(err);
svn_error_clear(err);
return SVN_NO_ERROR;
}
if (props)
{
const svn_string_t *value;
value = svn_hash_gets(props, SVN_PROP_IGNORE);
if (value)
svn_cstring_split_append(*patterns, value->data, "\n\r", FALSE,
result_pool);
value = svn_hash_gets(props, SVN_PROP_INHERITABLE_IGNORES);
if (value)
svn_cstring_split_append(*patterns, value->data, "\n\r", FALSE,
result_pool);
}
for (i = 0; i < inherited_props->nelts; i++)
{
svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
inherited_props, i, svn_prop_inherited_item_t *);
const svn_string_t *value;
value = svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_IGNORES);
if (value)
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 (svn_hash_gets(externals, local_abspath))
return TRUE;
for (hi = apr_hash_first(scratch_pool, externals);
hi;
hi = apr_hash_next(hi))
{
const char *external_abspath = apr_hash_this_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__internal_status_t *status;
const char *base_name = svn_dirent_basename(local_abspath, NULL);
is_ignored = svn_wc_match_ignore_list(base_name, 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->s.node_status = svn_wc_status_external;
if (status->s.conflicted)
is_ignored = FALSE;
if (no_ignore
|| !is_ignored
|| is_external)
return svn_error_trace((*status_func)(status_baton, local_abspath,
&status->s, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
get_dir_status(const struct walk_status_baton *wb,
const char *local_abspath,
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);
static svn_error_t *
one_child_status(const struct walk_status_baton *wb,
const char *local_abspath,
const char *parent_abspath,
const struct svn_wc__db_info_t *info,
const svn_io_dirent2_t *dirent,
const char *dir_repos_root_url,
const char *dir_repos_relpath,
const char *dir_repos_uuid,
svn_boolean_t unversioned_tree_conflicted,
apr_array_header_t **collected_ignore_patterns,
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 *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t conflicted = info ? info->conflicted
: unversioned_tree_conflicted;
if (info
&& info->status != svn_wc__db_status_not_present
&& info->status != svn_wc__db_status_excluded
&& info->status != svn_wc__db_status_server_excluded
&& !(info->kind == svn_node_unknown
&& info->status == svn_wc__db_status_normal))
{
if (depth == svn_depth_files
&& info->kind == svn_node_dir)
{
return SVN_NO_ERROR;
}
SVN_ERR(send_status_structure(wb, local_abspath,
dir_repos_root_url,
dir_repos_relpath,
dir_repos_uuid,
info, dirent, get_all,
status_func, status_baton,
scratch_pool));
if (depth == svn_depth_infinity
&& info->has_descendants )
{
SVN_ERR(get_dir_status(wb, local_abspath, TRUE,
dir_repos_root_url, dir_repos_relpath,
dir_repos_uuid, info,
dirent, ignore_patterns,
svn_depth_infinity, get_all,
no_ignore,
status_func, status_baton,
cancel_func, cancel_baton,
scratch_pool));
}
return SVN_NO_ERROR;
}
if (! conflicted)
{
if (dirent == NULL && strcmp(wb->target_abspath, local_abspath) != 0)
return SVN_NO_ERROR;
if (depth == svn_depth_files && dirent && dirent->kind == svn_node_dir)
return SVN_NO_ERROR;
if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL),
scratch_pool))
return SVN_NO_ERROR;
}
if (ignore_patterns && ! *collected_ignore_patterns)
SVN_ERR(collect_ignore_patterns(collected_ignore_patterns,
wb->db, parent_abspath, ignore_patterns,
result_pool, scratch_pool));
SVN_ERR(send_unversioned_item(wb,
local_abspath,
dirent,
conflicted,
*collected_ignore_patterns,
no_ignore,
status_func, status_baton,
scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
get_dir_status(const struct walk_status_baton *wb,
const char *local_abspath,
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 *sorted_children;
apr_array_header_t *collected_ignore_patterns = NULL;
apr_pool_t *iterpool;
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(scratch_pool);
if (wb->check_working_copy)
{
err = svn_io_get_dirents3(&dirents, local_abspath,
wb->ignore_text_mods ,
scratch_pool, 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(scratch_pool);
}
else
SVN_ERR(err);
}
else
dirents = apr_hash_make(scratch_pool);
if (!dir_info)
SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, local_abspath,
!wb->check_working_copy,
scratch_pool, 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,
scratch_pool, iterpool));
SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
wb->db, local_abspath,
!wb->check_working_copy,
scratch_pool, iterpool));
all_children = apr_hash_overlay(scratch_pool, nodes, dirents);
if (apr_hash_count(conflicts) > 0)
all_children = apr_hash_overlay(scratch_pool, conflicts, all_children);
if (! skip_this_dir)
{
if (dirent->special)
{
svn_io_dirent2_t *this_dirent = svn_io_dirent2_dup(dirent, iterpool);
SVN_ERR(svn_io_check_resolved_path(local_abspath,
&this_dirent->kind, iterpool));
this_dirent->special = FALSE;
SVN_ERR(send_status_structure(wb, local_abspath,
parent_repos_root_url,
parent_repos_relpath,
parent_repos_uuid,
dir_info, this_dirent, get_all,
status_func, status_baton,
iterpool));
}
else
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,
scratch_pool);
for (i = 0; i < sorted_children->nelts; i++)
{
const void *key;
apr_ssize_t klen;
svn_sort__item_t item;
const char *child_abspath;
svn_io_dirent2_t *child_dirent;
const struct svn_wc__db_info_t *child_info;
svn_pool_clear(iterpool);
item = APR_ARRAY_IDX(sorted_children, i, svn_sort__item_t);
key = item.key;
klen = item.klen;
child_abspath = svn_dirent_join(local_abspath, key, iterpool);
child_dirent = apr_hash_get(dirents, key, klen);
child_info = apr_hash_get(nodes, key, klen);
SVN_ERR(one_child_status(wb,
child_abspath,
local_abspath,
child_info,
child_dirent,
dir_repos_root_url,
dir_repos_relpath,
dir_repos_uuid,
apr_hash_get(conflicts, key, klen) != NULL,
&collected_ignore_patterns,
ignore_patterns,
depth,
get_all,
no_ignore,
status_func,
status_baton,
cancel_func,
cancel_baton,
scratch_pool,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
get_child_status(const struct walk_status_baton *wb,
const char *local_abspath,
const struct svn_wc__db_info_t *info,
const svn_io_dirent2_t *dirent,
const apr_array_header_t *ignore_patterns,
svn_boolean_t get_all,
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;
const struct svn_wc__db_info_t *dir_info;
apr_array_header_t *collected_ignore_patterns = NULL;
const char *parent_abspath = svn_dirent_dirname(local_abspath,
scratch_pool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
if (dirent->kind == svn_node_none)
dirent = NULL;
SVN_ERR(svn_wc__db_read_single_info(&dir_info,
wb->db, parent_abspath,
!wb->check_working_copy,
scratch_pool, scratch_pool));
SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url,
&dir_repos_uuid, dir_info,
NULL, NULL, NULL,
wb->db, parent_abspath,
scratch_pool, scratch_pool));
SVN_ERR(one_child_status(wb,
local_abspath,
parent_abspath,
info,
dirent,
dir_repos_root_url,
dir_repos_relpath,
dir_repos_uuid,
FALSE,
&collected_ignore_patterns,
ignore_patterns,
svn_depth_empty,
get_all,
TRUE,
status_func,
status_baton,
cancel_func,
cancel_baton,
scratch_pool,
scratch_pool));
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);
void *new_status = svn_wc_dup_status3(status, hash_pool);
const svn_wc__internal_status_t *old_status = (const void*)status;
svn_wc__internal_status_t *is = new_status;
is->has_descendants = old_status->has_descendants;
is->op_root = old_status->op_root;
assert(! svn_hash_gets(stat_hash, path));
svn_hash_sets(stat_hash, apr_pstrdup(hash_pool, path), new_status);
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,
svn_boolean_t check_working_copy,
const char *local_abspath,
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 = svn_hash_gets(statushash, local_abspath);
if (! statstruct)
{
svn_wc__internal_status_t *i_stat;
if (repos_node_status != svn_wc_status_added)
return SVN_NO_ERROR;
SVN_ERR(internal_status(&i_stat, db, local_abspath,
check_working_copy, pool, scratch_pool));
statstruct = &i_stat->s;
statstruct->repos_lock = repos_lock;
svn_hash_sets(statushash, apr_pstrdup(pool, local_abspath), 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->s.repos_root_url;
statstruct->repos_uuid =
b->edit_baton->anchor_status->s.repos_uuid;
}
if (statstruct->repos_node_status == svn_wc_status_deleted)
{
statstruct->ood_kind = statstruct->kind;
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->s.repos_root_url;
statstruct->repos_uuid =
b->edit_baton->anchor_status->s.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->s.repos_relpath;
else
{
const char *repos_relpath;
struct dir_baton *pb = db->parent_baton;
const svn_wc_status3_t *status = svn_hash_gets(pb->statii,
db->local_abspath);
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 *result_pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = edit_baton;
struct dir_baton *d;
const char *local_abspath;
const svn_wc__internal_status_t *status_in_parent;
apr_pool_t *dir_pool;
if (parent_baton)
dir_pool = svn_pool_create(parent_baton->pool);
else
dir_pool = svn_pool_create(result_pool);
d = apr_pcalloc(dir_pool, sizeof(*d));
SVN_ERR_ASSERT(path || (! pb));
if (pb)
local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
else
local_abspath = eb->anchor_abspath;
d->pool = dir_pool;
d->local_abspath = local_abspath;
d->name = path ? svn_dirent_basename(path, dir_pool) : NULL;
d->edit_baton = edit_baton;
d->parent_baton = parent_baton;
d->statii = apr_hash_make(dir_pool);
d->ood_changed_rev = SVN_INVALID_REVNUM;
d->ood_changed_date = 0;
d->repos_relpath = find_dir_repos_relpath(d, dir_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 = svn_hash_gets(pb->statii, d->local_abspath);
else
status_in_parent = eb->anchor_status;
if (status_in_parent
&& (status_in_parent->has_descendants)
&& (! 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, TRUE,
status_in_parent->s.repos_root_url,
NULL ,
status_in_parent->s.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,
dir_pool));
this_dir_status = svn_hash_gets(d->statii, d->local_abspath);
if (this_dir_status && this_dir_status->versioned
&& (d->depth == svn_depth_unknown
|| d->depth > status_in_parent->s.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__internal_status_t *i_status,
svn_boolean_t no_ignore,
svn_boolean_t get_all)
{
const svn_wc_status3_t *status = &i_status->s;
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)
&& !(status->node_status == svn_wc_status_deleted
&& !i_status->op_root))
return TRUE;
if (status->switched)
return TRUE;
if (status->versioned && status->lock)
return TRUE;
if (status->changelist)
return TRUE;
if (status->moved_to_abspath)
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 = apr_hash_this_key(hi);
svn_wc__internal_status_t *status = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
if (status->has_descendants
&& (depth == svn_depth_unknown
|| depth == svn_depth_infinity))
{
SVN_ERR(get_dir_status(&eb->wb,
local_abspath, 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->s.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->s,
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_ERR(tweak_statushash(db, db, TRUE, eb->db, eb->wb.check_working_copy,
local_abspath,
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, eb->wb.check_working_copy,
db->local_abspath,
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;
apr_pool_t *scratch_pool = db->pool;
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_none;
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, eb->wb.check_working_copy,
db->local_abspath,
repos_node_status, repos_text_status,
repos_prop_status, SVN_INVALID_REVNUM, NULL,
scratch_pool));
}
else
{
eb->anchor_status->s.repos_node_status = repos_node_status;
eb->anchor_status->s.repos_prop_status = repos_prop_status;
eb->anchor_status->s.repos_text_status = repos_text_status;
if (db->ood_changed_rev != eb->anchor_status->s.revision)
{
eb->anchor_status->s.ood_changed_rev = db->ood_changed_rev;
eb->anchor_status->s.ood_changed_date = db->ood_changed_date;
eb->anchor_status->s.ood_kind = db->ood_kind;
eb->anchor_status->s.ood_changed_author =
apr_pstrdup(pool, db->ood_changed_author);
}
}
}
if (pb && ! db->excluded)
{
svn_boolean_t was_deleted = FALSE;
svn_wc__internal_status_t *dir_status;
dir_status = svn_hash_gets(pb->statii, db->local_abspath);
if (dir_status &&
((dir_status->s.repos_node_status == svn_wc_status_deleted)
|| (dir_status->s.repos_node_status == svn_wc_status_replaced)))
was_deleted = TRUE;
SVN_ERR(handle_statii(eb,
dir_status ? dir_status->s.repos_root_url : NULL,
dir_status ? dir_status->s.repos_relpath : NULL,
dir_status ? dir_status->s.repos_uuid : NULL,
db->statii, was_deleted, db->depth, scratch_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->s, scratch_pool));
svn_hash_sets(pb->statii, db->local_abspath, NULL);
}
else if (! pb)
{
if (*eb->target_basename)
{
const svn_wc__internal_status_t *tgt_status;
tgt_status = svn_hash_gets(db->statii, eb->target_abspath);
if (tgt_status)
{
if (tgt_status->has_descendants)
{
SVN_ERR(get_dir_status(&eb->wb,
eb->target_abspath, 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,
scratch_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->s, scratch_pool));
}
}
else
{
SVN_ERR(handle_statii(eb,
eb->anchor_status->s.repos_root_url,
eb->anchor_status->s.repos_relpath,
eb->anchor_status->s.repos_uuid,
db->statii, FALSE, eb->default_depth,
scratch_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->s, scratch_pool));
eb->anchor_status = NULL;
}
}
svn_pool_clear(scratch_pool);
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 = fb->text_changed ? svn_wc_status_modified
: 0 ;
repos_prop_status = fb->prop_changed ? svn_wc_status_modified
: 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 = svn_hash_gets(fb->edit_baton->wb.repos_locks,
svn_fspath__join("/", repos_relpath,
pool));
}
}
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->edit_baton->wb.check_working_copy,
fb->local_abspath, 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__internal_walk_status(eb->db,
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_editor(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 check_working_copy,
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;
struct svn_wc__shim_fetch_baton_t *sfb;
const svn_delta_editor_t *inner_editor;
svn_delta_shim_callbacks_t *shim_callbacks =
svn_delta_shim_callbacks_default(result_pool);
eb = apr_pcalloc(result_pool, sizeof(*eb));
eb->default_depth = depth;
eb->target_revision = edit_revision;
eb->db = wc_ctx->db;
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 = !check_working_copy;
eb->wb.check_working_copy = check_working_copy;
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,
check_working_copy, 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;
sfb = apr_palloc(result_pool, sizeof(*sfb));
sfb->db = wc_ctx->db;
sfb->base_abspath = eb->anchor_abspath;
sfb->fetch_base = FALSE;
shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
shim_callbacks->fetch_baton = sfb;
SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
NULL, NULL, shim_callbacks,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
stat_wc_dirent_case_sensitive(const svn_io_dirent2_t **dirent,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t is_wcroot;
SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath,
scratch_pool));
return svn_error_trace(
svn_io_stat_dirent2(dirent, local_abspath,
! is_wcroot ,
TRUE ,
result_pool, scratch_pool));
}
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 struct svn_wc__db_info_t *info;
svn_error_t *err;
wb.db = db;
wb.target_abspath = local_abspath;
wb.ignore_text_mods = ignore_text_mods;
wb.check_working_copy = TRUE;
wb.repos_root = NULL;
wb.repos_locks = NULL;
if (!ignore_patterns)
{
apr_array_header_t *ignores;
SVN_ERR(svn_wc_get_default_ignores(&ignores, NULL, scratch_pool));
ignore_patterns = ignores;
}
err = svn_wc__db_read_single_info(&info, db, local_abspath,
FALSE ,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
info = NULL;
}
else
return svn_error_trace(err);
wb.externals = apr_hash_make(scratch_pool);
SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE,
scratch_pool, scratch_pool));
}
else
{
SVN_ERR(svn_wc__db_externals_defined_below(&wb.externals,
db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(stat_wc_dirent_case_sensitive(&dirent, db, local_abspath,
scratch_pool, scratch_pool));
}
if (info
&& info->has_descendants
&& info->status != svn_wc__db_status_not_present
&& info->status != svn_wc__db_status_excluded
&& info->status != svn_wc__db_status_server_excluded)
{
SVN_ERR(get_dir_status(&wb,
local_abspath,
FALSE ,
NULL, NULL, NULL,
info,
dirent,
ignore_patterns,
depth,
get_all,
no_ignore,
status_func, status_baton,
cancel_func, cancel_baton,
scratch_pool));
}
else
{
err = get_child_status(&wb,
local_abspath,
info,
dirent,
ignore_patterns,
get_all,
status_func, status_baton,
cancel_func, cancel_baton,
scratch_pool);
if (!info && err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("The node '%s' was not found."),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
SVN_ERR(err);
}
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
? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG)
: 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__internal_status_t **status,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t check_working_copy,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_io_dirent2_t *dirent = NULL;
const char *parent_repos_relpath;
const char *parent_repos_root_url;
const char *parent_repos_uuid;
const struct svn_wc__db_info_t *info;
svn_boolean_t is_root = FALSE;
svn_error_t *err;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
err = svn_wc__db_read_single_info(&info, db, local_abspath,
!check_working_copy,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
info = NULL;
if (check_working_copy)
SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE,
scratch_pool, scratch_pool));
}
else if (check_working_copy)
SVN_ERR(stat_wc_dirent_case_sensitive(&dirent, db, local_abspath,
scratch_pool, scratch_pool));
if (!info
|| info->kind == svn_node_unknown
|| info->status == svn_wc__db_status_not_present
|| info->status == svn_wc__db_status_server_excluded
|| info->status == svn_wc__db_status_excluded)
return svn_error_trace(assemble_unversioned(status,
db, local_abspath,
dirent,
info ? info->conflicted : FALSE,
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)
{
const char *const parent_abspath = svn_dirent_dirname(local_abspath,
scratch_pool);
if (check_working_copy)
SVN_ERR(svn_wc__db_read_info(NULL, 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));
else
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL,
&parent_repos_relpath,
&parent_repos_root_url,
&parent_repos_uuid,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
db, parent_abspath,
result_pool, scratch_pool));
}
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,
info,
dirent,
TRUE ,
FALSE, check_working_copy,
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)
{
svn_wc__internal_status_t *stat;
SVN_ERR(internal_status(&stat, wc_ctx->db, local_abspath,
TRUE ,
result_pool, scratch_pool));
*status = &stat->s;
return SVN_NO_ERROR;
}
svn_wc_status3_t *
svn_wc_dup_status3(const svn_wc_status3_t *orig_stat,
apr_pool_t *pool)
{
svn_wc__internal_status_t *new_istat = apr_palloc(pool, sizeof(*new_istat));
svn_wc_status3_t *new_stat = &new_istat->s;
*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);
if (orig_stat->moved_from_abspath)
new_stat->moved_from_abspath
= apr_pstrdup(pool, orig_stat->moved_from_abspath);
if (orig_stat->moved_to_abspath)
new_stat->moved_to_abspath
= apr_pstrdup(pool, orig_stat->moved_to_abspath);
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));
}