#include <apr_uri.h>
#include <apr_md5.h>
#include "svn_checksum.h"
#include "svn_hash.h"
#include "svn_wc.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_io.h"
#include "svn_props.h"
#include "svn_private_config.h"
#include "client.h"
#include "private/svn_wc_private.h"
struct edit_baton {
const char *target;
svn_wc_context_t *wc_ctx;
svn_depth_t depth;
const svn_wc_diff_callbacks4_t *diff_callbacks;
void *diff_cmd_baton;
svn_boolean_t dry_run;
svn_ra_session_t *ra_session;
svn_revnum_t revision;
svn_revnum_t target_revision;
const char *empty_file;
apr_hash_t *empty_hash;
apr_hash_t *deleted_paths;
svn_wc_notify_func2_t notify_func;
void *notify_baton;
svn_boolean_t walk_deleted_repos_dirs;
svn_cancel_func_t cancel_func;
void *cancel_baton;
apr_pool_t *pool;
};
typedef struct deleted_path_notify_t
{
svn_node_kind_t kind;
svn_wc_notify_action_t action;
svn_wc_notify_state_t state;
svn_boolean_t tree_conflicted;
} deleted_path_notify_t;
struct dir_baton {
svn_boolean_t added;
svn_boolean_t tree_conflicted;
svn_boolean_t skip;
svn_boolean_t skip_children;
const char *path;
const char *wcpath;
struct dir_baton *dir_baton;
struct edit_baton *edit_baton;
apr_array_header_t *propchanges;
apr_hash_t *pristine_props;
apr_pool_t *pool;
};
struct file_baton {
svn_boolean_t added;
svn_boolean_t tree_conflicted;
svn_boolean_t skip;
const char *path;
const char *wcpath;
const char *path_start_revision;
apr_hash_t *pristine_props;
svn_revnum_t base_revision;
const char *path_end_revision;
svn_txdelta_window_handler_t apply_handler;
void *apply_baton;
struct edit_baton *edit_baton;
svn_checksum_t *start_md5_checksum;
unsigned char result_digest[APR_MD5_DIGESTSIZE];
svn_checksum_t *result_md5_checksum;
apr_array_header_t *propchanges;
apr_pool_t *pool;
};
static struct dir_baton *
make_dir_baton(const char *path,
struct dir_baton *parent_baton,
struct edit_baton *edit_baton,
svn_boolean_t added,
apr_pool_t *pool)
{
apr_pool_t *dir_pool = svn_pool_create(pool);
struct dir_baton *dir_baton = apr_pcalloc(dir_pool, sizeof(*dir_baton));
dir_baton->dir_baton = parent_baton;
dir_baton->edit_baton = edit_baton;
dir_baton->added = added;
dir_baton->tree_conflicted = FALSE;
dir_baton->skip = FALSE;
dir_baton->skip_children = FALSE;
dir_baton->pool = dir_pool;
dir_baton->path = apr_pstrdup(dir_pool, path);
dir_baton->wcpath = svn_dirent_join(edit_baton->target, path, dir_pool);
dir_baton->propchanges = apr_array_make(pool, 1, sizeof(svn_prop_t));
return dir_baton;
}
static struct file_baton *
make_file_baton(const char *path,
svn_boolean_t added,
struct edit_baton *edit_baton,
apr_pool_t *pool)
{
apr_pool_t *file_pool = svn_pool_create(pool);
struct file_baton *file_baton = apr_pcalloc(file_pool, sizeof(*file_baton));
file_baton->edit_baton = edit_baton;
file_baton->added = added;
file_baton->tree_conflicted = FALSE;
file_baton->skip = FALSE;
file_baton->pool = file_pool;
file_baton->path = apr_pstrdup(file_pool, path);
file_baton->wcpath = svn_dirent_join(edit_baton->target, path, file_pool);
file_baton->propchanges = apr_array_make(pool, 1, sizeof(svn_prop_t));
file_baton->base_revision = edit_baton->revision;
return file_baton;
}
static void
get_file_mime_types(const char **mimetype1,
const char **mimetype2,
struct file_baton *b)
{
*mimetype1 = NULL;
*mimetype2 = NULL;
if (b->pristine_props)
{
svn_string_t *pristine_val;
pristine_val = apr_hash_get(b->pristine_props, SVN_PROP_MIME_TYPE,
strlen(SVN_PROP_MIME_TYPE));
if (pristine_val)
*mimetype2 = *mimetype1 = pristine_val->data;
}
if (b->propchanges)
{
int i;
svn_prop_t *propchange;
for (i = 0; i < b->propchanges->nelts; i++)
{
propchange = &APR_ARRAY_IDX(b->propchanges, i, svn_prop_t);
if (strcmp(propchange->name, SVN_PROP_MIME_TYPE) == 0)
{
if (propchange->value)
*mimetype2 = propchange->value->data;
break;
}
}
}
}
static svn_error_t *
get_file_from_ra(struct file_baton *b,
svn_boolean_t props_only,
apr_pool_t *scratch_pool)
{
if (! props_only)
{
svn_stream_t *fstream;
SVN_ERR(svn_stream_open_unique(&fstream, &(b->path_start_revision), NULL,
svn_io_file_del_on_pool_cleanup, b->pool,
b->pool));
fstream = svn_stream_checksummed2(fstream, NULL, &b->start_md5_checksum,
svn_checksum_md5, TRUE, b->pool);
SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
b->path,
b->base_revision,
fstream, NULL,
&(b->pristine_props),
b->pool));
SVN_ERR(svn_stream_close(fstream));
}
else
{
SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
b->path,
b->base_revision,
NULL, NULL,
&(b->pristine_props),
b->pool));
}
return SVN_NO_ERROR;
}
static void
remove_non_prop_changes(apr_hash_t *pristine_props,
apr_array_header_t *changes)
{
int i;
for (i = 0; i < changes->nelts; i++)
{
svn_prop_t *change = &APR_ARRAY_IDX(changes, i, svn_prop_t);
if (change->value)
{
const svn_string_t *old_val = apr_hash_get(pristine_props,
change->name,
APR_HASH_KEY_STRING);
if (old_val && svn_string_compare(old_val, change->value))
{
int j;
for (j = i; j < changes->nelts - 1; j++)
{
APR_ARRAY_IDX(changes, j, svn_prop_t)
= APR_ARRAY_IDX(changes, j+1, svn_prop_t);
}
changes->nelts--;
}
}
}
}
static svn_error_t *
get_dirprops_from_ra(struct dir_baton *b, svn_revnum_t base_revision)
{
return svn_ra_get_dir2(b->edit_baton->ra_session,
NULL, NULL, &(b->pristine_props),
b->path,
base_revision,
0,
b->pool);
}
static svn_error_t *
get_empty_file(struct edit_baton *eb,
const char **empty_file_path)
{
if (!eb->empty_file)
SVN_ERR(svn_io_open_unique_file3(NULL, &(eb->empty_file), NULL,
svn_io_file_del_on_pool_cleanup,
eb->pool, eb->pool));
*empty_file_path = eb->empty_file;
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 **root_baton)
{
struct edit_baton *eb = edit_baton;
struct dir_baton *b = make_dir_baton("", NULL, eb, FALSE, pool);
b->wcpath = apr_pstrdup(pool, eb->target);
SVN_ERR(get_dirprops_from_ra(b, base_revision));
*root_baton = b;
return SVN_NO_ERROR;
}
static svn_error_t *
diff_deleted_dir(const char *dir,
svn_revnum_t revision,
svn_ra_session_t *ra_session,
struct edit_baton *eb,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
apr_hash_t *dirents;
apr_pool_t *iterpool = svn_pool_create(pool);
apr_hash_index_t *hi;
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(svn_ra_get_dir2(ra_session,
&dirents,
NULL, NULL,
dir,
revision,
SVN_DIRENT_KIND,
pool));
for (hi = apr_hash_first(pool, dirents); hi;
hi = apr_hash_next(hi))
{
const char *path;
const char *name = svn__apr_hash_index_key(hi);
svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
svn_pool_clear(iterpool);
path = svn_relpath_join(dir, name, iterpool);
if (dirent->kind == svn_node_file)
{
struct file_baton *b;
const char *mimetype1, *mimetype2;
b = make_file_baton(path, FALSE, eb, iterpool);
SVN_ERR(get_file_from_ra(b, FALSE, iterpool));
SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
get_file_mime_types(&mimetype1, &mimetype2, b);
SVN_ERR(eb->diff_callbacks->file_deleted(
NULL, NULL, b->wcpath,
b->path_start_revision,
b->path_end_revision,
mimetype1, mimetype2,
b->pristine_props,
b->edit_baton->diff_cmd_baton,
iterpool));
}
if (dirent->kind == svn_node_dir)
SVN_ERR(diff_deleted_dir(path,
revision,
ra_session,
eb,
cancel_func,
cancel_baton,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t base_revision,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
svn_node_kind_t kind;
svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
svn_wc_notify_action_t action = svn_wc_notify_skip;
svn_boolean_t tree_conflicted = FALSE;
apr_pool_t *scratch_pool;
if (pb->skip || pb->tree_conflicted || pb->skip_children)
return SVN_NO_ERROR;
scratch_pool = svn_pool_create(eb->pool);
SVN_ERR(svn_ra_check_path(eb->ra_session, path, eb->revision, &kind,
scratch_pool));
switch (kind)
{
case svn_node_file:
{
const char *mimetype1, *mimetype2;
struct file_baton *b;
b = make_file_baton(path, FALSE, eb, scratch_pool);
SVN_ERR(get_file_from_ra(b, FALSE, scratch_pool));
SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
get_file_mime_types(&mimetype1, &mimetype2, b);
SVN_ERR(eb->diff_callbacks->file_deleted(
&state, &tree_conflicted, b->wcpath,
b->path_start_revision,
b->path_end_revision,
mimetype1, mimetype2,
b->pristine_props,
b->edit_baton->diff_cmd_baton,
scratch_pool));
break;
}
case svn_node_dir:
{
SVN_ERR(eb->diff_callbacks->dir_deleted(
&state, &tree_conflicted,
svn_dirent_join(eb->target, path, pool),
eb->diff_cmd_baton, scratch_pool));
if (eb->walk_deleted_repos_dirs)
{
SVN_ERR(diff_deleted_dir(path,
eb->revision,
eb->ra_session,
eb,
eb->cancel_func,
eb->cancel_baton,
scratch_pool));
}
break;
}
default:
break;
}
if ((state != svn_wc_notify_state_missing)
&& (state != svn_wc_notify_state_obstructed)
&& !tree_conflicted)
{
action = svn_wc_notify_update_delete;
}
if (eb->notify_func)
{
const char* deleted_path;
deleted_path_notify_t *dpn = apr_pcalloc(eb->pool, sizeof(*dpn));
deleted_path = svn_dirent_join(eb->target, path, eb->pool);
dpn->kind = kind;
dpn->action = tree_conflicted ? svn_wc_notify_tree_conflict : action;
dpn->state = state;
dpn->tree_conflicted = tree_conflicted;
apr_hash_set(eb->deleted_paths, deleted_path, APR_HASH_KEY_STRING, dpn);
}
svn_pool_destroy(scratch_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 *b;
svn_wc_notify_state_t state;
b = make_dir_baton(path, pb, eb, TRUE, pool);
b->pristine_props = eb->empty_hash;
*child_baton = b;
if (pb->skip || pb->tree_conflicted || pb->skip_children)
{
b->skip = TRUE;
return SVN_NO_ERROR;
}
SVN_ERR(eb->diff_callbacks->dir_added(
&state, &b->tree_conflicted,
&b->skip, &b->skip_children, b->wcpath,
eb->target_revision, copyfrom_path, copyfrom_revision,
eb->diff_cmd_baton, pool));
if (eb->notify_func)
{
deleted_path_notify_t *dpn;
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
svn_node_kind_t kind = svn_node_dir;
dpn = apr_hash_get(eb->deleted_paths, b->wcpath, APR_HASH_KEY_STRING);
if (dpn)
{
apr_hash_set(eb->deleted_paths, b->wcpath,
APR_HASH_KEY_STRING, NULL);
kind = dpn->kind;
state = dpn->state;
}
if (b->tree_conflicted)
action = svn_wc_notify_tree_conflict;
else if (dpn)
{
if (dpn->action == svn_wc_notify_update_delete)
action = svn_wc_notify_update_replace;
else
action = dpn->action;
}
else if (state == svn_wc_notify_state_missing ||
state == svn_wc_notify_state_obstructed)
action = svn_wc_notify_skip;
else
action = svn_wc_notify_update_add;
notify = svn_wc_create_notify(b->wcpath, action, pool);
notify->kind = kind;
notify->content_state = notify->prop_state = state;
(*eb->notify_func)(eb->notify_baton, notify, pool);
}
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;
struct edit_baton *eb = pb->edit_baton;
struct dir_baton *b;
b = make_dir_baton(path, pb, pb->edit_baton, FALSE, pool);
*child_baton = b;
if (pb->skip || pb->tree_conflicted || pb->skip_children)
{
b->skip = TRUE;
return SVN_NO_ERROR;
}
SVN_ERR(get_dirprops_from_ra(b, base_revision));
SVN_ERR(eb->diff_callbacks->dir_opened(
&b->tree_conflicted, &b->skip,
&b->skip_children, b->wcpath, base_revision,
b->edit_baton->diff_cmd_baton, 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 *b;
b = make_file_baton(path, TRUE, pb->edit_baton, pool);
*file_baton = b;
if (pb->skip || pb->tree_conflicted || pb->skip_children)
{
b->skip = TRUE;
return SVN_NO_ERROR;
}
b->pristine_props = pb->edit_baton->empty_hash;
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 *b;
struct edit_baton *eb = pb->edit_baton;
b = make_file_baton(path, FALSE, pb->edit_baton, pool);
*file_baton = b;
if (pb->skip || pb->tree_conflicted || pb->skip_children)
{
b->skip = TRUE;
return SVN_NO_ERROR;
}
b->base_revision = base_revision;
SVN_ERR(eb->diff_callbacks->file_opened(
&b->tree_conflicted, &b->skip,
b->wcpath, base_revision, eb->diff_cmd_baton, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
window_handler(svn_txdelta_window_t *window,
void *window_baton)
{
struct file_baton *b = window_baton;
SVN_ERR(b->apply_handler(window, b->apply_baton));
if (!window)
{
b->result_md5_checksum = svn_checksum__from_digest(b->result_digest,
svn_checksum_md5,
b->pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_md5_digest,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
struct file_baton *b = file_baton;
svn_stream_t *src_stream;
svn_stream_t *result_stream;
apr_pool_t *scratch_pool = b->pool;
if (b->skip)
{
*handler = svn_delta_noop_window_handler;
*handler_baton = NULL;
return SVN_NO_ERROR;
}
if (!b->added)
SVN_ERR(get_file_from_ra(b, FALSE, b->pool));
else
SVN_ERR(get_empty_file(b->edit_baton, &(b->path_start_revision)));
SVN_ERR_ASSERT(b->path_start_revision != NULL);
if (base_md5_digest != NULL)
{
svn_checksum_t *base_md5_checksum;
SVN_ERR(svn_checksum_parse_hex(&base_md5_checksum, svn_checksum_md5,
base_md5_digest, scratch_pool));
if (!svn_checksum_match(base_md5_checksum, b->start_md5_checksum))
return svn_error_trace(svn_checksum_mismatch_err(
base_md5_checksum,
b->start_md5_checksum,
scratch_pool,
_("Base checksum mismatch for '%s'"),
b->path));
}
SVN_ERR(svn_stream_open_readonly(&src_stream, b->path_start_revision,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_open_unique(&result_stream, &b->path_end_revision, NULL,
svn_io_file_del_on_pool_cleanup,
scratch_pool, scratch_pool));
svn_txdelta_apply(src_stream,
result_stream,
b->result_digest,
b->path, b->pool,
&(b->apply_handler), &(b->apply_baton));
*handler = window_handler;
*handler_baton = file_baton;
return SVN_NO_ERROR;
}
static svn_error_t *
close_file(void *file_baton,
const char *expected_md5_digest,
apr_pool_t *pool)
{
struct file_baton *b = file_baton;
struct edit_baton *eb = b->edit_baton;
svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
apr_pool_t *scratch_pool;
if (b->skip)
{
svn_pool_destroy(b->pool);
return SVN_NO_ERROR;
}
scratch_pool = b->pool;
if (expected_md5_digest)
{
svn_checksum_t *expected_md5_checksum;
SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
expected_md5_digest, scratch_pool));
if (!svn_checksum_match(expected_md5_checksum, b->result_md5_checksum))
return svn_error_trace(svn_checksum_mismatch_err(
expected_md5_checksum,
b->result_md5_checksum,
pool,
_("Checksum mismatch for '%s'"),
b->path));
}
if (!b->added && b->propchanges->nelts > 0)
{
if (!b->pristine_props)
{
SVN_ERR(get_file_from_ra(b, TRUE, scratch_pool));
}
remove_non_prop_changes(b->pristine_props, b->propchanges);
}
if (b->path_end_revision || b->propchanges->nelts > 0)
{
const char *mimetype1, *mimetype2;
get_file_mime_types(&mimetype1, &mimetype2, b);
if (b->added)
SVN_ERR(eb->diff_callbacks->file_added(
&content_state, &prop_state, &b->tree_conflicted,
b->wcpath,
b->path_end_revision ? b->path_start_revision : NULL,
b->path_end_revision,
0,
b->edit_baton->target_revision,
mimetype1, mimetype2,
NULL, SVN_INVALID_REVNUM,
b->propchanges, b->pristine_props,
b->edit_baton->diff_cmd_baton,
scratch_pool));
else
SVN_ERR(eb->diff_callbacks->file_changed(
&content_state, &prop_state,
&b->tree_conflicted, b->wcpath,
b->path_end_revision ? b->path_start_revision : NULL,
b->path_end_revision,
b->edit_baton->revision,
b->edit_baton->target_revision,
mimetype1, mimetype2,
b->propchanges, b->pristine_props,
b->edit_baton->diff_cmd_baton,
scratch_pool));
}
if (eb->notify_func)
{
deleted_path_notify_t *dpn;
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
svn_node_kind_t kind = svn_node_file;
dpn = apr_hash_get(eb->deleted_paths, b->wcpath, APR_HASH_KEY_STRING);
if (dpn)
{
apr_hash_set(eb->deleted_paths, b->wcpath,
APR_HASH_KEY_STRING, NULL);
kind = dpn->kind;
content_state = prop_state = dpn->state;
}
if (b->tree_conflicted)
action = svn_wc_notify_tree_conflict;
else if (dpn)
{
if (dpn->action == svn_wc_notify_update_delete
&& b->added)
action = svn_wc_notify_update_replace;
else
action = dpn->action;
}
else if ((content_state == svn_wc_notify_state_missing)
|| (content_state == svn_wc_notify_state_obstructed))
action = svn_wc_notify_skip;
else if (b->added)
action = svn_wc_notify_update_add;
else
action = svn_wc_notify_update_update;
notify = svn_wc_create_notify(b->wcpath, action, scratch_pool);
notify->kind = kind;
notify->content_state = content_state;
notify->prop_state = prop_state;
(*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
}
svn_pool_destroy(b->pool);
return SVN_NO_ERROR;
}
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
struct dir_baton *b = dir_baton;
struct edit_baton *eb = b->edit_baton;
svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
svn_boolean_t skipped = FALSE;
apr_pool_t *scratch_pool;
if (b->skip)
{
svn_pool_destroy(b->pool);
return SVN_NO_ERROR;
}
scratch_pool = b->pool;
if (!b->added && b->propchanges->nelts > 0)
remove_non_prop_changes(b->pristine_props, b->propchanges);
if (b->propchanges->nelts > 0)
{
svn_boolean_t tree_conflicted = FALSE;
SVN_ERR(eb->diff_callbacks->dir_props_changed(
&prop_state, &tree_conflicted,
b->wcpath, b->added,
b->propchanges, b->pristine_props,
b->edit_baton->diff_cmd_baton, scratch_pool));
if (tree_conflicted)
b->tree_conflicted = TRUE;
if (prop_state == svn_wc_notify_state_obstructed
|| prop_state == svn_wc_notify_state_missing)
{
content_state = prop_state;
skipped = TRUE;
}
}
SVN_ERR(eb->diff_callbacks->dir_closed(NULL, NULL, NULL,
b->wcpath, b->added,
b->edit_baton->diff_cmd_baton,
scratch_pool));
if (!skipped && !b->added && eb->notify_func)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(pool, eb->deleted_paths); hi;
hi = apr_hash_next(hi))
{
svn_wc_notify_t *notify;
const char *deleted_path = svn__apr_hash_index_key(hi);
deleted_path_notify_t *dpn = svn__apr_hash_index_val(hi);
notify = svn_wc_create_notify(deleted_path, dpn->action, pool);
notify->kind = dpn->kind;
notify->content_state = notify->prop_state = dpn->state;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
(*eb->notify_func)(eb->notify_baton, notify, pool);
apr_hash_set(eb->deleted_paths, deleted_path,
APR_HASH_KEY_STRING, NULL);
}
}
if (!b->added && eb->notify_func)
{
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
if (b->tree_conflicted)
action = svn_wc_notify_tree_conflict;
else if (skipped)
action = svn_wc_notify_skip;
else
action = svn_wc_notify_update_update;
notify = svn_wc_create_notify(b->wcpath, action, pool);
notify->kind = svn_node_dir;
notify->content_state = content_state;
notify->prop_state = prop_state;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
(*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
}
svn_pool_destroy(b->pool);
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 *b = file_baton;
svn_prop_t *propchange;
if (b->skip)
return SVN_NO_ERROR;
propchange = apr_array_push(b->propchanges);
propchange->name = apr_pstrdup(b->pool, name);
propchange->value = value ? svn_string_dup(value, b->pool) : NULL;
return SVN_NO_ERROR;
}
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;
svn_prop_t *propchange;
if (db->skip)
return SVN_NO_ERROR;
propchange = apr_array_push(db->propchanges);
propchange->name = apr_pstrdup(db->pool, name);
propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
return SVN_NO_ERROR;
}
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
svn_pool_destroy(eb->pool);
return SVN_NO_ERROR;
}
static svn_error_t *
absent_directory(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
if (eb->notify_func)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(svn_dirent_join(pb->wcpath,
svn_relpath_basename(path,
NULL),
pool),
svn_wc_notify_skip, pool);
notify->kind = svn_node_dir;
notify->content_state = notify->prop_state
= svn_wc_notify_state_missing;
(*eb->notify_func)(eb->notify_baton, notify, pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
absent_file(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
if (eb->notify_func)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(svn_dirent_join(pb->wcpath,
svn_relpath_basename(path,
pool),
pool),
svn_wc_notify_skip, pool);
notify->kind = svn_node_file;
notify->content_state = notify->prop_state
= svn_wc_notify_state_missing;
(*eb->notify_func)(eb->notify_baton, notify, pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_diff_editor(const svn_delta_editor_t **editor,
void **edit_baton,
svn_wc_context_t *wc_ctx,
const char *target,
svn_depth_t depth,
svn_ra_session_t *ra_session,
svn_revnum_t revision,
svn_boolean_t walk_deleted_dirs,
svn_boolean_t dry_run,
const svn_wc_diff_callbacks4_t *diff_callbacks,
void *diff_cmd_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *editor_pool = svn_pool_create(result_pool);
svn_delta_editor_t *tree_editor = svn_delta_default_editor(editor_pool);
struct edit_baton *eb = apr_pcalloc(editor_pool, sizeof(*eb));
const char *target_abspath;
SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, editor_pool));
eb->pool = editor_pool;
eb->target = target;
eb->wc_ctx = wc_ctx;
eb->depth = depth;
eb->diff_callbacks = diff_callbacks;
eb->diff_cmd_baton = diff_cmd_baton;
eb->dry_run = dry_run;
eb->ra_session = ra_session;
eb->revision = revision;
eb->empty_file = NULL;
eb->empty_hash = apr_hash_make(eb->pool);
eb->deleted_paths = apr_hash_make(eb->pool);
eb->notify_func = notify_func;
eb->notify_baton = notify_baton;
eb->walk_deleted_repos_dirs = walk_deleted_dirs;
eb->cancel_func = cancel_func;
eb->cancel_baton = cancel_baton;
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->add_file = add_file;
tree_editor->open_file = open_file;
tree_editor->apply_textdelta = apply_textdelta;
tree_editor->close_file = close_file;
tree_editor->close_directory = close_directory;
tree_editor->change_file_prop = change_file_prop;
tree_editor->change_dir_prop = change_dir_prop;
tree_editor->close_edit = close_edit;
tree_editor->absent_directory = absent_directory;
tree_editor->absent_file = absent_file;
return svn_delta_get_cancellation_editor(cancel_func,
cancel_baton,
tree_editor,
eb,
editor,
edit_baton,
eb->pool);
}