#include <apr_hash.h>
#include <apr_md5.h>
#include "svn_error.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "private/svn_wc_private.h"
#include "wc.h"
#include "props.h"
#include "adm_files.h"
#include "translate.h"
#include "svn_private_config.h"
static void
reverse_propchanges(apr_hash_t *baseprops,
apr_array_header_t *propchanges,
apr_pool_t *pool)
{
int i;
for (i = 0; i < propchanges->nelts; i++)
{
svn_prop_t *propchange
= &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
const svn_string_t *original_value =
apr_hash_get(baseprops, propchange->name, APR_HASH_KEY_STRING);
if ((original_value == NULL) && (propchange->value != NULL))
{
apr_hash_set(baseprops, propchange->name, APR_HASH_KEY_STRING,
svn_string_dup(propchange->value, pool));
propchange->value = NULL;
}
else if ((original_value != NULL) && (propchange->value == NULL))
{
propchange->value = svn_string_dup(original_value, pool);
apr_hash_set(baseprops, propchange->name, APR_HASH_KEY_STRING,
NULL);
}
else if ((original_value != NULL) && (propchange->value != NULL))
{
const svn_string_t *str = svn_string_dup(propchange->value, pool);
propchange->value = svn_string_dup(original_value, pool);
apr_hash_set(baseprops, propchange->name, APR_HASH_KEY_STRING, str);
}
}
}
static svn_error_t *
get_pristine_file(const char **result_abspath,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t use_base,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_checksum_t *checksum;
if (!use_base)
{
SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL, NULL,
&checksum, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
}
else
{
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &checksum,
NULL, NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
}
if (checksum != NULL)
{
SVN_ERR(svn_wc__db_pristine_get_path(result_abspath, db, local_abspath,
checksum,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
*result_abspath = NULL;
return SVN_NO_ERROR;
}
struct edit_baton {
svn_wc__db_t *db;
const char *target;
const char *anchor_abspath;
svn_revnum_t revnum;
svn_boolean_t root_opened;
const svn_wc_diff_callbacks4_t *callbacks;
void *callback_baton;
svn_depth_t depth;
svn_boolean_t ignore_ancestry;
svn_boolean_t show_copies_as_adds;
svn_boolean_t use_git_diff_format;
svn_boolean_t use_text_base;
svn_boolean_t reverse_order;
const char *empty_file;
apr_hash_t *changelist_hash;
svn_cancel_func_t cancel_func;
void *cancel_baton;
apr_pool_t *pool;
};
struct dir_baton {
svn_boolean_t added;
svn_depth_t depth;
const char *name;
const char *local_abspath;
const char *path;
apr_hash_t *compared;
apr_array_header_t *propchanges;
struct edit_baton *eb;
apr_pool_t *pool;
};
struct file_baton {
svn_boolean_t added;
const char *name;
const char *local_abspath;
const char *path;
const char *temp_file_path;
apr_array_header_t *propchanges;
const svn_checksum_t *base_checksum;
svn_checksum_t *result_checksum;
struct edit_baton *eb;
apr_pool_t *pool;
};
static svn_error_t *
make_edit_baton(struct edit_baton **edit_baton,
svn_wc__db_t *db,
const char *anchor_abspath,
const char *target,
const svn_wc_diff_callbacks4_t *callbacks,
void *callback_baton,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t show_copies_as_adds,
svn_boolean_t use_git_diff_format,
svn_boolean_t use_text_base,
svn_boolean_t reverse_order,
const apr_array_header_t *changelist_filter,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
apr_hash_t *changelist_hash = NULL;
struct edit_baton *eb;
SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
if (changelist_filter && changelist_filter->nelts)
SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
pool));
eb = apr_pcalloc(pool, sizeof(*eb));
eb->db = db;
eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
eb->target = apr_pstrdup(pool, target);
eb->callbacks = callbacks;
eb->callback_baton = callback_baton;
eb->depth = depth;
eb->ignore_ancestry = ignore_ancestry;
eb->show_copies_as_adds = show_copies_as_adds;
eb->use_git_diff_format = use_git_diff_format;
eb->use_text_base = use_text_base;
eb->reverse_order = reverse_order;
eb->changelist_hash = changelist_hash;
eb->cancel_func = cancel_func;
eb->cancel_baton = cancel_baton;
eb->pool = pool;
*edit_baton = eb;
return SVN_NO_ERROR;
}
static struct dir_baton *
make_dir_baton(const char *path,
struct dir_baton *parent_baton,
struct edit_baton *eb,
svn_boolean_t added,
svn_depth_t depth,
apr_pool_t *result_pool)
{
apr_pool_t *dir_pool = svn_pool_create(result_pool);
struct dir_baton *db = apr_pcalloc(dir_pool, sizeof(*db));
db->eb = eb;
db->added = added;
db->depth = depth;
db->pool = dir_pool;
db->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
db->compared = apr_hash_make(dir_pool);
db->path = apr_pstrdup(dir_pool, path);
db->name = svn_dirent_basename(db->path, NULL);
if (parent_baton != NULL)
db->local_abspath = svn_dirent_join(parent_baton->local_abspath, db->name,
dir_pool);
else
db->local_abspath = apr_pstrdup(dir_pool, eb->anchor_abspath);
return db;
}
static struct file_baton *
make_file_baton(const char *path,
svn_boolean_t added,
struct dir_baton *parent_baton,
apr_pool_t *result_pool)
{
apr_pool_t *file_pool = svn_pool_create(result_pool);
struct file_baton *fb = apr_pcalloc(file_pool, sizeof(*fb));
struct edit_baton *eb = parent_baton->eb;
fb->eb = eb;
fb->added = added;
fb->pool = file_pool;
fb->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
fb->path = apr_pstrdup(file_pool, path);
fb->name = svn_dirent_basename(fb->path, NULL);
fb->local_abspath = svn_dirent_join(parent_baton->local_abspath, fb->name,
file_pool);
return fb;
}
static svn_error_t *
get_empty_file(struct edit_baton *b,
const char **empty_file)
{
if (!b->empty_file)
{
SVN_ERR(svn_io_open_unique_file3(NULL, &b->empty_file, NULL,
svn_io_file_del_on_pool_cleanup,
b->pool, b->pool));
}
*empty_file = b->empty_file;
return SVN_NO_ERROR;
}
static const char *
get_prop_mimetype(apr_hash_t *props)
{
return svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
}
static apr_hash_t *
apply_propchanges(apr_hash_t *props,
const apr_array_header_t *propchanges)
{
apr_hash_t *newprops = apr_hash_copy(apr_hash_pool_get(props), props);
int i;
for (i = 0; i < propchanges->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
apr_hash_set(newprops, prop->name, APR_HASH_KEY_STRING, prop->value);
}
return newprops;
}
static svn_error_t *
file_diff(struct edit_baton *eb,
const char *local_abspath,
const char *path,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = eb->db;
const char *textbase;
const char *empty_file;
svn_boolean_t replaced;
svn_wc__db_status_t status;
const char *original_repos_relpath;
svn_revnum_t revision;
svn_revnum_t revert_base_revnum;
svn_boolean_t have_base;
svn_wc__db_status_t base_status;
svn_boolean_t use_base = FALSE;
SVN_ERR_ASSERT(! eb->use_text_base);
if (! svn_wc__internal_changelist_match(db, local_abspath,
eb->changelist_hash, scratch_pool))
return SVN_NO_ERROR;
SVN_ERR(svn_wc__db_read_info(&status, NULL, &revision, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL,
&have_base, NULL, NULL,
db, local_abspath, scratch_pool, scratch_pool));
if (have_base)
SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &revert_base_revnum,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
replaced = ((status == svn_wc__db_status_added)
&& have_base
&& base_status != svn_wc__db_status_not_present);
if (status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL,
&original_repos_relpath, NULL, NULL,
NULL, db, local_abspath,
scratch_pool, scratch_pool));
if (replaced
&& ! (status == svn_wc__db_status_copied
|| status == svn_wc__db_status_moved_here))
{
use_base = TRUE;
revision = revert_base_revnum;
}
SVN_ERR(get_pristine_file(&textbase, db, local_abspath,
use_base, scratch_pool, scratch_pool));
SVN_ERR(get_empty_file(eb, &empty_file));
if ((! replaced && status == svn_wc__db_status_deleted) ||
(replaced && ! eb->ignore_ancestry))
{
const char *base_mimetype;
apr_hash_t *baseprops;
SVN_ERR(svn_wc__get_pristine_props(&baseprops, db, local_abspath,
scratch_pool, scratch_pool));
if (baseprops)
base_mimetype = get_prop_mimetype(baseprops);
else
base_mimetype = NULL;
SVN_ERR(eb->callbacks->file_deleted(NULL, NULL, path,
textbase,
empty_file,
base_mimetype,
NULL,
baseprops,
eb->callback_baton,
scratch_pool));
if (! (replaced && ! eb->ignore_ancestry))
{
return SVN_NO_ERROR;
}
}
if ((! replaced && status == svn_wc__db_status_added) ||
(replaced && ! eb->ignore_ancestry) ||
((status == svn_wc__db_status_copied ||
status == svn_wc__db_status_moved_here) &&
(eb->show_copies_as_adds || eb->use_git_diff_format)))
{
const char *translated = NULL;
const char *working_mimetype;
apr_hash_t *baseprops;
apr_hash_t *workingprops;
apr_array_header_t *propchanges;
SVN_ERR(svn_wc__get_actual_props(&workingprops, db, local_abspath,
scratch_pool, scratch_pool));
working_mimetype = get_prop_mimetype(workingprops);
baseprops = apr_hash_make(scratch_pool);
SVN_ERR(svn_prop_diffs(&propchanges, workingprops, baseprops,
scratch_pool));
SVN_ERR(svn_wc__internal_translated_file(
&translated, local_abspath, db, local_abspath,
SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
eb->cancel_func, eb->cancel_baton,
scratch_pool, scratch_pool));
SVN_ERR(eb->callbacks->file_added(NULL, NULL, NULL, path,
(! eb->show_copies_as_adds &&
eb->use_git_diff_format &&
status != svn_wc__db_status_added) ?
textbase : empty_file,
translated,
0, revision,
NULL,
working_mimetype,
original_repos_relpath,
SVN_INVALID_REVNUM, propchanges,
baseprops, eb->callback_baton,
scratch_pool));
}
else
{
const char *translated = NULL;
apr_hash_t *baseprops;
const char *base_mimetype;
const char *working_mimetype;
apr_hash_t *workingprops;
apr_array_header_t *propchanges;
svn_boolean_t modified;
SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, local_abspath,
FALSE, scratch_pool));
if (modified)
{
SVN_ERR(svn_wc__internal_translated_file(
&translated, local_abspath, db, local_abspath,
SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
eb->cancel_func, eb->cancel_baton,
scratch_pool, scratch_pool));
}
if (replaced
&& eb->ignore_ancestry)
{
SVN_ERR(svn_wc__db_base_get_props(&baseprops,
db, local_abspath,
scratch_pool, scratch_pool));
}
else
{
SVN_ERR_ASSERT(!replaced
|| status == svn_wc__db_status_copied
|| status == svn_wc__db_status_moved_here);
SVN_ERR(svn_wc__get_pristine_props(&baseprops, db, local_abspath,
scratch_pool, scratch_pool));
if (!baseprops)
baseprops = apr_hash_make(scratch_pool);
}
base_mimetype = get_prop_mimetype(baseprops);
SVN_ERR(svn_wc__get_actual_props(&workingprops, db, local_abspath,
scratch_pool, scratch_pool));
working_mimetype = get_prop_mimetype(workingprops);
SVN_ERR(svn_prop_diffs(&propchanges, workingprops, baseprops, scratch_pool));
if (modified || propchanges->nelts > 0)
{
SVN_ERR(eb->callbacks->file_changed(NULL, NULL, NULL,
path,
modified ? textbase : NULL,
translated,
revision,
SVN_INVALID_REVNUM,
base_mimetype,
working_mimetype,
propchanges, baseprops,
eb->callback_baton,
scratch_pool));
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
walk_local_nodes_diff(struct edit_baton *eb,
const char *local_abspath,
const char *path,
svn_depth_t depth,
apr_hash_t *compared,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = eb->db;
const apr_array_header_t *children;
int i;
svn_boolean_t in_anchor_not_target;
apr_pool_t *iterpool;
if (eb->use_text_base)
return SVN_NO_ERROR;
in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
if (svn_wc__internal_changelist_match(db, local_abspath,
eb->changelist_hash, scratch_pool)
&& (! in_anchor_not_target)
&& (!compared || ! apr_hash_get(compared, path, 0)))
{
svn_boolean_t modified;
SVN_ERR(svn_wc__props_modified(&modified, db, local_abspath,
scratch_pool));
if (modified)
{
apr_array_header_t *propchanges;
apr_hash_t *baseprops;
SVN_ERR(svn_wc__internal_propdiff(&propchanges, &baseprops,
db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(eb->callbacks->dir_props_changed(NULL, NULL,
path, FALSE ,
propchanges, baseprops,
eb->callback_baton,
scratch_pool));
}
}
if (depth == svn_depth_empty && !in_anchor_not_target)
return SVN_NO_ERROR;
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
scratch_pool, iterpool));
for (i = 0; i < children->nelts; i++)
{
const char *name = APR_ARRAY_IDX(children, i, const char*);
const char *child_abspath, *child_path;
svn_wc__db_status_t status;
svn_wc__db_kind_t kind;
svn_pool_clear(iterpool);
if (eb->cancel_func)
SVN_ERR(eb->cancel_func(eb->cancel_baton));
if (in_anchor_not_target && strcmp(eb->target, name))
continue;
child_abspath = svn_dirent_join(local_abspath, name, iterpool);
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
db, child_abspath,
iterpool, iterpool));
if (status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_excluded
|| status == svn_wc__db_status_server_excluded)
continue;
child_path = svn_relpath_join(path, name, iterpool);
if (compared && apr_hash_get(compared, child_path, APR_HASH_KEY_STRING))
continue;
switch (kind)
{
case svn_wc__db_kind_file:
case svn_wc__db_kind_symlink:
SVN_ERR(file_diff(eb, child_abspath, child_path, iterpool));
break;
case svn_wc__db_kind_dir:
if (in_anchor_not_target
|| (depth > svn_depth_files)
|| (depth == svn_depth_unknown))
{
svn_depth_t depth_below_here = depth;
if (depth_below_here == svn_depth_immediates)
depth_below_here = svn_depth_empty;
SVN_ERR(walk_local_nodes_diff(eb,
child_abspath,
child_path,
depth_below_here,
NULL,
iterpool));
}
break;
default:
break;
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
report_wc_file_as_added(struct edit_baton *eb,
const char *local_abspath,
const char *path,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = eb->db;
apr_hash_t *emptyprops;
const char *mimetype;
apr_hash_t *wcprops = NULL;
apr_array_header_t *propchanges;
const char *empty_file;
const char *source_file;
const char *translated_file;
svn_wc__db_status_t status;
svn_revnum_t revision;
if (! svn_wc__internal_changelist_match(db, local_abspath,
eb->changelist_hash, scratch_pool))
return SVN_NO_ERROR;
SVN_ERR(get_empty_file(eb, &empty_file));
SVN_ERR(svn_wc__db_read_info(&status, NULL, &revision, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
if (status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR_ASSERT(status != svn_wc__db_status_deleted || eb->use_text_base);
if (status == svn_wc__db_status_copied ||
status == svn_wc__db_status_moved_here)
{
if (eb->use_text_base)
return SVN_NO_ERROR;
return file_diff(eb, local_abspath, path, scratch_pool);
}
emptyprops = apr_hash_make(scratch_pool);
if (eb->use_text_base)
SVN_ERR(svn_wc__get_pristine_props(&wcprops, db, local_abspath,
scratch_pool, scratch_pool));
else
SVN_ERR(svn_wc__get_actual_props(&wcprops, db, local_abspath,
scratch_pool, scratch_pool));
mimetype = get_prop_mimetype(wcprops);
SVN_ERR(svn_prop_diffs(&propchanges,
wcprops, emptyprops, scratch_pool));
if (eb->use_text_base)
SVN_ERR(get_pristine_file(&source_file, db, local_abspath,
FALSE, scratch_pool, scratch_pool));
else
source_file = local_abspath;
SVN_ERR(svn_wc__internal_translated_file(
&translated_file, source_file, db, local_abspath,
SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
eb->cancel_func, eb->cancel_baton,
scratch_pool, scratch_pool));
SVN_ERR(eb->callbacks->file_added(NULL, NULL, NULL,
path,
empty_file, translated_file,
0, revision,
NULL, mimetype,
NULL, SVN_INVALID_REVNUM,
propchanges, emptyprops,
eb->callback_baton,
scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
report_wc_directory_as_added(struct edit_baton *eb,
const char *local_abspath,
const char *path,
svn_depth_t depth,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = eb->db;
apr_hash_t *emptyprops, *wcprops = NULL;
apr_array_header_t *propchanges;
const apr_array_header_t *children;
int i;
apr_pool_t *iterpool;
emptyprops = apr_hash_make(scratch_pool);
if (svn_wc__internal_changelist_match(db, local_abspath,
eb->changelist_hash, scratch_pool))
{
if (eb->use_text_base)
SVN_ERR(svn_wc__get_pristine_props(&wcprops, db, local_abspath,
scratch_pool, scratch_pool));
else
SVN_ERR(svn_wc__get_actual_props(&wcprops, db, local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_prop_diffs(&propchanges, wcprops, emptyprops, scratch_pool));
if (propchanges->nelts > 0)
SVN_ERR(eb->callbacks->dir_props_changed(NULL, NULL,
path, TRUE,
propchanges, emptyprops,
eb->callback_baton,
scratch_pool));
}
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
scratch_pool, iterpool));
for (i = 0; i < children->nelts; i++)
{
const char *name = APR_ARRAY_IDX(children, i, const char *);
const char *child_abspath, *child_path;
svn_wc__db_status_t status;
svn_wc__db_kind_t kind;
svn_pool_clear(iterpool);
if (eb->cancel_func)
SVN_ERR(eb->cancel_func(eb->cancel_baton));
child_abspath = svn_dirent_join(local_abspath, name, iterpool);
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
db, child_abspath, iterpool, iterpool));
if (status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_excluded
|| status == svn_wc__db_status_server_excluded)
{
continue;
}
if (!eb->use_text_base && status == svn_wc__db_status_deleted)
continue;
child_path = svn_relpath_join(path, name, iterpool);
switch (kind)
{
case svn_wc__db_kind_file:
case svn_wc__db_kind_symlink:
SVN_ERR(report_wc_file_as_added(eb, child_abspath, child_path,
iterpool));
break;
case svn_wc__db_kind_dir:
if (depth > svn_depth_files || depth == svn_depth_unknown)
{
svn_depth_t depth_below_here = depth;
if (depth_below_here == svn_depth_immediates)
depth_below_here = svn_depth_empty;
SVN_ERR(report_wc_directory_as_added(eb,
child_abspath,
child_path,
depth_below_here,
iterpool));
}
break;
default:
break;
}
}
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->revnum = target_revision;
return SVN_NO_ERROR;
}
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **root_baton)
{
struct edit_baton *eb = edit_baton;
struct dir_baton *db;
eb->root_opened = TRUE;
db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
*root_baton = db;
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->eb;
svn_wc__db_t *db = eb->db;
const char *empty_file;
const char *name = svn_dirent_basename(path, NULL);
const char *local_abspath = svn_dirent_join(pb->local_abspath, name, pool);
svn_wc__db_status_t status;
svn_wc__db_kind_t kind;
apr_hash_set(pb->compared, apr_pstrdup(pb->pool, path),
APR_HASH_KEY_STRING, "");
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
db, local_abspath, pool, pool));
if (!eb->use_text_base && status == svn_wc__db_status_deleted)
return SVN_NO_ERROR;
SVN_ERR(get_empty_file(pb->eb, &empty_file));
switch (kind)
{
case svn_wc__db_kind_file:
case svn_wc__db_kind_symlink:
if (eb->reverse_order)
{
const char *textbase;
apr_hash_t *baseprops = NULL;
const char *base_mimetype;
SVN_ERR(get_pristine_file(&textbase, db, local_abspath,
eb->use_text_base, pool, pool));
SVN_ERR(svn_wc__get_pristine_props(&baseprops, eb->db, local_abspath,
pool, pool));
base_mimetype = get_prop_mimetype(baseprops);
SVN_ERR(eb->callbacks->file_deleted(NULL, NULL, path,
textbase,
empty_file,
base_mimetype,
NULL,
baseprops,
eb->callback_baton,
pool));
}
else
{
SVN_ERR(report_wc_file_as_added(eb, local_abspath, path, pool));
}
break;
case svn_wc__db_kind_dir:
SVN_ERR(report_wc_directory_as_added(eb,
local_abspath,
path,
svn_depth_infinity,
pool));
default:
break;
}
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 *dir_pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct dir_baton *db;
svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
? svn_depth_empty : pb->depth;
db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
dir_pool);
*child_baton = db;
return SVN_NO_ERROR;
}
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct dir_baton *db;
svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
? svn_depth_empty : pb->depth;
db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
*child_baton = db;
apr_hash_set(pb->compared, apr_pstrdup(pb->pool, db->path),
APR_HASH_KEY_STRING, "");
SVN_ERR(db->eb->callbacks->dir_opened(NULL, NULL, NULL,
path, base_revision,
db->eb->callback_baton, dir_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
struct dir_baton *db = dir_baton;
struct edit_baton *eb = db->eb;
apr_pool_t *scratch_pool = db->pool;
if (db->propchanges->nelts > 0)
{
apr_hash_t *originalprops;
if (db->added)
{
originalprops = apr_hash_make(scratch_pool);
}
else
{
if (db->eb->use_text_base)
{
SVN_ERR(svn_wc__get_pristine_props(&originalprops,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
}
else
{
apr_hash_t *base_props, *repos_props;
SVN_ERR(svn_wc__get_actual_props(&originalprops,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__get_pristine_props(&base_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
repos_props = apply_propchanges(base_props, db->propchanges);
SVN_ERR(svn_prop_diffs(&db->propchanges, repos_props,
originalprops, scratch_pool));
}
}
if (!eb->reverse_order)
reverse_propchanges(originalprops, db->propchanges, db->pool);
SVN_ERR(eb->callbacks->dir_props_changed(NULL, NULL,
db->path,
db->added,
db->propchanges,
originalprops,
eb->callback_baton,
scratch_pool));
apr_hash_set(db->compared, db->path, 0, "");
}
if (!db->added)
SVN_ERR(walk_local_nodes_diff(eb,
db->local_abspath,
db->path,
db->depth,
db->compared,
scratch_pool));
SVN_ERR(db->eb->callbacks->dir_closed(NULL, NULL, NULL, db->path,
db->added, db->eb->callback_baton,
scratch_pool));
svn_pool_destroy(db->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 *file_pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct file_baton *fb;
fb = make_file_baton(path, TRUE, pb, file_pool);
*file_baton = 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 *file_pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->eb;
struct file_baton *fb;
fb = make_file_baton(path, FALSE, pb, file_pool);
*file_baton = fb;
apr_hash_set(pb->compared, apr_pstrdup(pb->pool, path),
APR_HASH_KEY_STRING, "");
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, &fb->base_checksum, NULL,
NULL, NULL, NULL,
eb->db, fb->local_abspath,
fb->pool, fb->pool));
SVN_ERR(eb->callbacks->file_opened(NULL, NULL, fb->path, base_revision,
eb->callback_baton, fb->pool));
return SVN_NO_ERROR;
}
struct window_handler_baton
{
struct file_baton *fb;
svn_txdelta_window_handler_t apply_handler;
void *apply_baton;
unsigned char result_digest[APR_MD5_DIGESTSIZE];
};
static svn_error_t *
window_handler(svn_txdelta_window_t *window,
void *window_baton)
{
struct window_handler_baton *whb = window_baton;
struct file_baton *fb = whb->fb;
SVN_ERR(whb->apply_handler(window, whb->apply_baton));
if (!window)
{
fb->result_checksum = svn_checksum__from_digest(whb->result_digest,
svn_checksum_md5,
fb->pool);
}
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;
struct window_handler_baton *whb;
struct edit_baton *eb = fb->eb;
svn_stream_t *source;
svn_stream_t *temp_stream;
if (fb->base_checksum)
SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
eb->db, fb->local_abspath,
fb->base_checksum,
pool, pool));
else
source = svn_stream_empty(pool);
SVN_ERR(svn_stream_open_unique(&temp_stream, &fb->temp_file_path, NULL,
svn_io_file_del_on_pool_cleanup,
fb->pool, fb->pool));
whb = apr_pcalloc(fb->pool, sizeof(*whb));
whb->fb = fb;
svn_txdelta_apply(source, temp_stream,
whb->result_digest,
fb->path ,
fb->pool,
&whb->apply_handler, &whb->apply_baton);
*handler = window_handler;
*handler_baton = whb;
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 *fb = file_baton;
struct edit_baton *eb = fb->eb;
svn_wc__db_t *db = eb->db;
apr_pool_t *scratch_pool = fb->pool;
svn_wc__db_status_t status;
const char *empty_file;
svn_error_t *err;
const svn_checksum_t *pristine_checksum;
const char *pristine_file;
apr_hash_t *pristine_props;
const char *repos_file;
apr_hash_t *repos_props;
const char *repos_mimetype;
svn_boolean_t had_props, props_mod;
const char *localfile;
svn_boolean_t modified;
apr_hash_t *originalprops;
if (expected_md5_digest != NULL)
{
svn_checksum_t *expected_checksum;
const svn_checksum_t *repos_checksum = fb->result_checksum;
SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
expected_md5_digest, scratch_pool));
if (repos_checksum == NULL)
repos_checksum = fb->base_checksum;
if (repos_checksum->kind != svn_checksum_md5)
SVN_ERR(svn_wc__db_pristine_get_md5(&repos_checksum,
eb->db, fb->local_abspath,
repos_checksum,
scratch_pool, scratch_pool));
if (!svn_checksum_match(expected_checksum, repos_checksum))
return svn_checksum_mismatch_err(
expected_checksum,
repos_checksum,
pool,
_("Checksum mismatch for '%s'"),
svn_dirent_local_style(fb->local_abspath,
scratch_pool));
}
err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, &pristine_checksum, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, &had_props, &props_mod,
NULL, NULL, NULL,
db, fb->local_abspath,
scratch_pool, scratch_pool);
if (fb->added
&& err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
status = svn_wc__db_status_not_present;
pristine_checksum = NULL;
had_props = FALSE;
props_mod = FALSE;
}
else
SVN_ERR(err);
SVN_ERR(get_empty_file(eb, &empty_file));
if (fb->added)
{
pristine_props = apr_hash_make(scratch_pool);
pristine_file = empty_file;
}
else
{
if (status != svn_wc__db_status_normal)
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
&pristine_checksum,
NULL, NULL,
&had_props, NULL,
db, fb->local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
db, fb->local_abspath,
pristine_checksum,
scratch_pool, scratch_pool));
if (had_props)
SVN_ERR(svn_wc__db_base_get_props(&pristine_props,
db, fb->local_abspath,
scratch_pool, scratch_pool));
else
pristine_props = apr_hash_make(scratch_pool);
}
if (status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, eb->db,
fb->local_abspath,
scratch_pool, scratch_pool));
repos_props = apply_propchanges(pristine_props, fb->propchanges);
repos_mimetype = get_prop_mimetype(repos_props);
repos_file = fb->temp_file_path ? fb->temp_file_path : pristine_file;
if (fb->added || (!eb->use_text_base && status == svn_wc__db_status_deleted))
{
if (eb->reverse_order)
return eb->callbacks->file_added(NULL, NULL, NULL, fb->path,
empty_file,
repos_file,
0,
eb->revnum,
NULL,
repos_mimetype,
NULL, SVN_INVALID_REVNUM,
fb->propchanges,
apr_hash_make(pool),
eb->callback_baton,
scratch_pool);
else
return eb->callbacks->file_deleted(NULL, NULL, fb->path,
repos_file,
empty_file,
repos_mimetype,
NULL,
repos_props,
eb->callback_baton,
scratch_pool);
}
if ((status == svn_wc__db_status_copied ||
status == svn_wc__db_status_moved_here) && eb->show_copies_as_adds)
return eb->callbacks->file_added(NULL, NULL, NULL, fb->path,
empty_file,
fb->local_abspath,
0,
eb->revnum,
NULL,
repos_mimetype,
NULL, SVN_INVALID_REVNUM,
fb->propchanges,
apr_hash_make(pool),
eb->callback_baton,
scratch_pool);
modified = (fb->temp_file_path != NULL);
if (!modified && !eb->use_text_base)
SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
fb->local_abspath,
FALSE, scratch_pool));
if (modified)
{
if (eb->use_text_base)
SVN_ERR(get_pristine_file(&localfile, eb->db, fb->local_abspath,
FALSE, scratch_pool, scratch_pool));
else
SVN_ERR(svn_wc__internal_translated_file(
&localfile, fb->local_abspath, eb->db, fb->local_abspath,
SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
eb->cancel_func, eb->cancel_baton,
scratch_pool, scratch_pool));
}
else
localfile = repos_file = NULL;
if (eb->use_text_base)
{
originalprops = pristine_props;
}
else
{
SVN_ERR(svn_wc__get_actual_props(&originalprops,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_prop_diffs(&fb->propchanges,
repos_props, originalprops, scratch_pool));
}
if (localfile || fb->propchanges->nelts > 0)
{
const char *original_mimetype = get_prop_mimetype(originalprops);
if (fb->propchanges->nelts > 0
&& ! eb->reverse_order)
reverse_propchanges(originalprops, fb->propchanges, scratch_pool);
SVN_ERR(eb->callbacks->file_changed(NULL, NULL, NULL,
fb->path,
eb->reverse_order ? localfile
: repos_file,
eb->reverse_order
? repos_file
: localfile,
eb->reverse_order
? SVN_INVALID_REVNUM
: eb->revnum,
eb->reverse_order
? eb->revnum
: SVN_INVALID_REVNUM,
eb->reverse_order
? original_mimetype
: repos_mimetype,
eb->reverse_order
? repos_mimetype
: original_mimetype,
fb->propchanges, originalprops,
eb->callback_baton,
scratch_pool));
}
svn_pool_destroy(fb->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 *fb = file_baton;
svn_prop_t *propchange;
propchange = apr_array_push(fb->propchanges);
propchange->name = apr_pstrdup(fb->pool, name);
propchange->value = value ? svn_string_dup(value, fb->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;
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;
if (!eb->root_opened)
{
SVN_ERR(walk_local_nodes_diff(eb,
eb->anchor_abspath,
"",
eb->depth,
NULL,
eb->pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_get_diff_editor6(const svn_delta_editor_t **editor,
void **edit_baton,
svn_wc_context_t *wc_ctx,
const char *anchor_abspath,
const char *target,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t show_copies_as_adds,
svn_boolean_t use_git_diff_format,
svn_boolean_t use_text_base,
svn_boolean_t reverse_order,
svn_boolean_t server_performs_filtering,
const apr_array_header_t *changelist_filter,
const svn_wc_diff_callbacks4_t *callbacks,
void *callback_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb;
void *inner_baton;
svn_delta_editor_t *tree_editor;
const svn_delta_editor_t *inner_editor;
SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
SVN_ERR(make_edit_baton(&eb,
wc_ctx->db,
anchor_abspath, target,
callbacks, callback_baton,
depth, ignore_ancestry, show_copies_as_adds,
use_git_diff_format,
use_text_base, reverse_order, changelist_filter,
cancel_func, cancel_baton,
result_pool));
tree_editor = svn_delta_default_editor(eb->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->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->change_dir_prop = change_dir_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 == svn_depth_unknown)
SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
&inner_baton,
wc_ctx->db,
anchor_abspath,
target,
inner_editor,
inner_baton,
result_pool));
return svn_delta_get_cancellation_editor(cancel_func,
cancel_baton,
inner_editor,
inner_baton,
editor,
edit_baton,
result_pool);
}