#include <stdlib.h>
#include <string.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_md5.h>
#include <apr_tables.h>
#include <apr_strings.h>
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_delta.h"
#include "svn_string.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_error.h"
#include "svn_io.h"
#include "svn_private_config.h"
#include "svn_time.h"
#include "wc.h"
#include "adm_files.h"
#include "entries.h"
#include "translate.h"
#include "workqueue.h"
#include "private/svn_wc_private.h"
#define IS_NODE_PRESENT(status) \
((status) != svn_wc__db_status_server_excluded &&\
(status) != svn_wc__db_status_excluded && \
(status) != svn_wc__db_status_not_present)
static svn_error_t *
path_join_under_root(const char **result_path,
const char *base_path,
const char *add_path,
apr_pool_t *result_pool);
struct edit_baton
{
const char *target_basename;
const char *anchor_abspath;
const char *target_abspath;
svn_wc__db_t *db;
const apr_array_header_t *ext_patterns;
svn_revnum_t *target_revision;
svn_depth_t requested_depth;
svn_boolean_t depth_is_sticky;
svn_boolean_t use_commit_times;
svn_boolean_t root_opened;
svn_boolean_t target_deleted;
svn_boolean_t allow_unver_obstructions;
svn_boolean_t adds_as_modification;
svn_boolean_t clean_checkout;
const char *switch_relpath;
const char *repos_root;
const char *repos_uuid;
const char *diff3_cmd;
svn_wc_external_update_t external_func;
void *external_baton;
svn_wc_notify_func2_t notify_func;
void *notify_baton;
svn_cancel_func_t cancel_func;
void *cancel_baton;
svn_wc_conflict_resolver_func2_t conflict_func;
void *conflict_baton;
apr_hash_t *skipped_trees;
apr_hash_t *dir_dirents;
const char *wcroot_abspath;
apr_pool_t *pool;
};
static svn_error_t *
remember_skipped_tree(struct edit_baton *eb,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
apr_hash_set(eb->skipped_trees,
apr_pstrdup(eb->pool,
svn_dirent_skip_ancestor(eb->wcroot_abspath,
local_abspath)),
APR_HASH_KEY_STRING, (void*)1);
return SVN_NO_ERROR;
}
struct dir_baton
{
const char *name;
const char *local_abspath;
const char *new_relpath;
svn_revnum_t old_revision;
struct edit_baton *edit_baton;
struct dir_baton *parent_baton;
svn_boolean_t skip_this;
svn_boolean_t already_notified;
svn_boolean_t adding_dir;
svn_boolean_t shadowed;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
apr_hash_t *deletion_conflicts;
apr_hash_t *not_present_files;
svn_boolean_t obstruction_found;
svn_boolean_t add_existed;
apr_array_header_t *propchanges;
svn_boolean_t edited;
svn_wc_conflict_description2_t *edit_conflict;
struct bump_dir_info *bump_info;
svn_depth_t ambient_depth;
svn_boolean_t was_incomplete;
apr_pool_t *pool;
};
struct bump_dir_info
{
struct bump_dir_info *parent;
int ref_count;
apr_pool_t *pool;
};
struct handler_baton
{
svn_txdelta_window_handler_t apply_handler;
void *apply_baton;
apr_pool_t *pool;
struct file_baton *fb;
const char *new_text_base_tmp_abspath;
svn_checksum_t *expected_source_checksum;
svn_checksum_t *actual_source_checksum;
svn_stream_t *source_checksum_stream;
unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE];
svn_checksum_t * new_text_base_sha1_checksum;
};
static svn_error_t *
get_empty_tmp_file(const char **tmp_filename,
svn_wc__db_t *db,
const char *wri_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *temp_dir_abspath;
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
svn_io_file_del_none,
scratch_pool, scratch_pool));
return SVN_NO_ERROR;
}
static apr_status_t
cleanup_edit_baton(void *edit_baton)
{
struct edit_baton *eb = edit_baton;
svn_error_t *err;
apr_pool_t *pool = apr_pool_parent_get(eb->pool);
err = svn_wc__wq_run(eb->db, eb->wcroot_abspath,
NULL , NULL ,
pool);
if (err)
{
apr_status_t apr_err = err->apr_err;
svn_error_clear(err);
return apr_err;
}
return APR_SUCCESS;
}
static apr_status_t
cleanup_edit_baton_child(void *edit_baton)
{
struct edit_baton *eb = edit_baton;
apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
return APR_SUCCESS;
}
static svn_error_t *
make_dir_baton(struct dir_baton **d_p,
const char *path,
struct edit_baton *eb,
struct dir_baton *pb,
svn_boolean_t adding,
apr_pool_t *scratch_pool)
{
apr_pool_t *dir_pool;
struct dir_baton *d;
struct bump_dir_info *bdi;
if (pb != NULL)
dir_pool = svn_pool_create(pb->pool);
else
dir_pool = svn_pool_create(eb->pool);
SVN_ERR_ASSERT(path || (! pb));
d = apr_pcalloc(dir_pool, sizeof(*d));
if (path)
{
d->name = svn_dirent_basename(path, dir_pool);
SVN_ERR(path_join_under_root(&d->local_abspath,
pb->local_abspath, d->name, dir_pool));
}
else
{
d->name = NULL;
d->local_abspath = eb->anchor_abspath;
}
if (eb->switch_relpath)
{
if (pb == NULL)
{
if (*eb->target_basename == '\0')
{
d->new_relpath = eb->switch_relpath;
}
else
{
SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
eb->db, d->local_abspath,
dir_pool, scratch_pool));
}
}
else
{
if (pb->parent_baton == NULL
&& strcmp(eb->target_basename, d->name) == 0)
d->new_relpath = eb->switch_relpath;
else
d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
dir_pool);
}
}
else
{
if (adding)
{
SVN_ERR_ASSERT(pb != NULL);
d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
dir_pool);
}
else
{
SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
eb->db, d->local_abspath,
dir_pool, scratch_pool));
SVN_ERR_ASSERT(d->new_relpath);
}
}
bdi = apr_pcalloc(dir_pool, sizeof(*bdi));
bdi->parent = pb ? pb->bump_info : NULL;
bdi->ref_count = 1;
bdi->pool = dir_pool;
if (pb)
++bdi->parent->ref_count;
d->edit_baton = eb;
d->parent_baton = pb;
d->pool = dir_pool;
d->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
d->obstruction_found = FALSE;
d->add_existed = FALSE;
d->bump_info = bdi;
d->old_revision = SVN_INVALID_REVNUM;
d->adding_dir = adding;
d->changed_rev = SVN_INVALID_REVNUM;
d->not_present_files = apr_hash_make(dir_pool);
if (pb)
{
d->skip_this = pb->skip_this;
d->shadowed = pb->shadowed;
}
d->ambient_depth = svn_depth_unknown;
d->was_incomplete = FALSE;
*d_p = d;
return SVN_NO_ERROR;
}
static svn_error_t *
already_in_a_tree_conflict(svn_boolean_t *conflicted,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
static void
do_notification(const struct edit_baton *eb,
const char *local_abspath,
svn_node_kind_t kind,
svn_wc_notify_action_t action,
apr_pool_t *scratch_pool)
{
svn_wc_notify_t *notify;
if (eb->notify_func == NULL)
return;
notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
notify->kind = kind;
(*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
}
static svn_error_t *
maybe_release_dir_info(struct bump_dir_info *bdi)
{
while (bdi != NULL)
{
apr_pool_t *destroy_pool;
if (--bdi->ref_count > 0)
break;
destroy_pool = bdi->pool;
bdi = bdi->parent;
svn_pool_destroy(destroy_pool);
}
return SVN_NO_ERROR;
}
struct file_baton
{
apr_pool_t *pool;
const char *name;
const char *local_abspath;
const char *new_relpath;
svn_revnum_t old_revision;
struct edit_baton *edit_baton;
struct dir_baton *dir_baton;
svn_boolean_t skip_this;
svn_boolean_t already_notified;
svn_boolean_t adding_file;
svn_boolean_t obstruction_found;
svn_boolean_t add_existed;
svn_boolean_t shadowed;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
const svn_checksum_t *new_text_base_md5_checksum;
const svn_checksum_t *new_text_base_sha1_checksum;
const svn_checksum_t *original_checksum;
apr_array_header_t *propchanges;
svn_boolean_t local_prop_mods;
struct bump_dir_info *bump_info;
svn_boolean_t edited;
svn_wc_conflict_description2_t *edit_conflict;
};
static svn_error_t *
make_file_baton(struct file_baton **f_p,
struct dir_baton *pb,
const char *path,
svn_boolean_t adding,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb = pb->edit_baton;
apr_pool_t *file_pool = svn_pool_create(pb->pool);
struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
SVN_ERR_ASSERT(path);
f->name = svn_dirent_basename(path, file_pool);
f->old_revision = SVN_INVALID_REVNUM;
SVN_ERR(path_join_under_root(&f->local_abspath,
pb->local_abspath, f->name, file_pool));
if (eb->switch_relpath)
{
if (pb->parent_baton == NULL
&& strcmp(eb->target_basename, f->name) == 0)
f->new_relpath = eb->switch_relpath;
else
f->new_relpath = svn_relpath_join(pb->new_relpath, f->name,
file_pool);
}
else
{
if (adding)
f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool);
else
{
SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL,
eb->db, f->local_abspath,
file_pool, scratch_pool));
SVN_ERR_ASSERT(f->new_relpath);
}
}
f->pool = file_pool;
f->edit_baton = pb->edit_baton;
f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
f->bump_info = pb->bump_info;
f->adding_file = adding;
f->obstruction_found = FALSE;
f->add_existed = FALSE;
f->skip_this = pb->skip_this;
f->shadowed = pb->shadowed;
f->dir_baton = pb;
f->changed_rev = SVN_INVALID_REVNUM;
++f->bump_info->ref_count;
*f_p = f;
return SVN_NO_ERROR;
}
static svn_error_t *
mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
{
if (db->edited)
return SVN_NO_ERROR;
if (db->parent_baton)
SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool));
db->edited = TRUE;
if (db->edit_conflict)
{
SVN_ERR(svn_wc__db_op_set_tree_conflict(db->edit_baton->db,
db->local_abspath,
db->edit_conflict,
scratch_pool));
do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
svn_wc_notify_tree_conflict, scratch_pool);
db->already_notified = TRUE;
}
return SVN_NO_ERROR;
}
static svn_error_t *
mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
{
if (fb->edited)
return SVN_NO_ERROR;
SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool));
fb->edited = TRUE;
if (fb->edit_conflict)
{
SVN_ERR(svn_wc__db_op_set_tree_conflict(fb->edit_baton->db,
fb->local_abspath,
fb->edit_conflict,
scratch_pool));
do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
svn_wc_notify_tree_conflict, scratch_pool);
fb->already_notified = TRUE;
}
return SVN_NO_ERROR;
}
static svn_error_t *
window_handler(svn_txdelta_window_t *window, void *baton)
{
struct handler_baton *hb = baton;
struct file_baton *fb = hb->fb;
svn_wc__db_t *db = fb->edit_baton->db;
svn_error_t *err;
err = hb->apply_handler(window, hb->apply_baton);
if (window != NULL && !err)
return SVN_NO_ERROR;
if (hb->expected_source_checksum)
{
svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream);
if (!err2)
{
SVN_ERR_ASSERT(hb->expected_source_checksum->kind ==
hb->actual_source_checksum->kind);
if (!svn_checksum_match(hb->expected_source_checksum,
hb->actual_source_checksum))
{
err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err,
_("Checksum mismatch while updating '%s':\n"
" expected: %s\n"
" actual: %s\n"),
svn_dirent_local_style(fb->local_abspath, hb->pool),
svn_checksum_to_cstring(hb->expected_source_checksum,
hb->pool),
svn_checksum_to_cstring(hb->actual_source_checksum,
hb->pool));
}
}
err = svn_error_compose_create(err, err2);
}
if (err)
{
svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath, TRUE,
hb->pool));
}
else
{
fb->new_text_base_md5_checksum =
svn_checksum__from_digest(hb->new_text_base_md5_digest,
svn_checksum_md5, fb->pool);
fb->new_text_base_sha1_checksum =
svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath,
fb->new_text_base_sha1_checksum,
fb->new_text_base_md5_checksum,
hb->pool));
}
svn_pool_destroy(hb->pool);
return err;
}
static svn_error_t *
accumulate_last_change(svn_revnum_t *changed_rev,
apr_time_t *changed_date,
const char **changed_author,
const apr_array_header_t *entry_props,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
*changed_rev = SVN_INVALID_REVNUM;
*changed_date = 0;
*changed_author = NULL;
for (i = 0; i < entry_props->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
if (! prop->value)
continue;
if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
*changed_author = apr_pstrdup(result_pool, prop->value->data);
else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
{
apr_int64_t rev;
SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
*changed_rev = (svn_revnum_t)rev;
}
else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
path_join_under_root(const char **result_path,
const char *base_path,
const char *add_path,
apr_pool_t *pool)
{
svn_boolean_t under_root;
SVN_ERR(svn_dirent_is_under_root(&under_root,
result_path, base_path, add_path, pool));
if (! under_root)
{
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Path '%s' is not in the working copy"),
svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
pool));
}
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;
struct dir_baton *db;
svn_boolean_t already_conflicted;
svn_error_t *err;
eb->root_opened = TRUE;
SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
*dir_baton = db;
err = already_in_a_tree_conflict(&already_conflicted, eb->db,
db->local_abspath, pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
already_conflicted = FALSE;
}
else if (already_conflicted)
{
db->skip_this = TRUE;
db->already_notified = TRUE;
do_notification(eb, eb->target_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted, pool);
return SVN_NO_ERROR;
}
if (*eb->target_basename == '\0')
{
svn_wc__db_status_t status;
SVN_ERR(svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
&db->changed_rev, &db->changed_date,
&db->changed_author, &db->ambient_depth,
NULL, NULL, NULL, NULL, NULL,
eb->db, db->local_abspath,
db->pool, pool));
db->was_incomplete = (status == svn_wc__db_status_incomplete);
SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
db->local_abspath,
db->new_relpath,
*eb->target_revision,
pool));
}
return SVN_NO_ERROR;
}
typedef struct modcheck_baton_t {
svn_wc__db_t *db;
svn_boolean_t found_mod;
svn_boolean_t found_not_delete;
} modcheck_baton_t;
static svn_error_t *
modcheck_callback(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *scratch_pool)
{
modcheck_baton_t *mb = baton;
switch (status->node_status)
{
case svn_wc_status_normal:
case svn_wc_status_incomplete:
case svn_wc_status_ignored:
case svn_wc_status_none:
case svn_wc_status_unversioned:
case svn_wc_status_external:
break;
case svn_wc_status_deleted:
mb->found_mod = TRUE;
break;
case svn_wc_status_missing:
case svn_wc_status_obstructed:
if (status->prop_status != svn_wc_status_modified)
break;
default:
case svn_wc_status_added:
case svn_wc_status_replaced:
case svn_wc_status_modified:
mb->found_mod = TRUE;
mb->found_not_delete = TRUE;
return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
}
return SVN_NO_ERROR;
}
static svn_error_t *
node_has_local_mods(svn_boolean_t *modified,
svn_boolean_t *all_edits_are_deletes,
svn_wc__db_t *db,
const char *local_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
svn_error_t *err;
modcheck_baton.db = db;
err = svn_wc__internal_walk_status(db, local_abspath,
svn_depth_infinity,
FALSE, FALSE, FALSE, NULL,
modcheck_callback, &modcheck_baton,
cancel_func, cancel_baton,
scratch_pool);
if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
svn_error_clear(err);
else
SVN_ERR(err);
*modified = modcheck_baton.found_mod;
*all_edits_are_deletes = !modcheck_baton.found_not_delete;
return SVN_NO_ERROR;
}
#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
static svn_error_t *
create_tree_conflict(svn_wc_conflict_description2_t **pconflict,
struct edit_baton *eb,
const char *local_abspath,
svn_wc_conflict_reason_t reason,
svn_wc_conflict_action_t action,
svn_node_kind_t their_node_kind,
const char *their_relpath,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
const char *repos_root_url = NULL;
const char *left_repos_relpath;
svn_revnum_t left_revision;
svn_node_kind_t left_kind;
const char *right_repos_relpath;
const char *added_repos_relpath = NULL;
svn_node_kind_t conflict_node_kind;
svn_wc_conflict_version_t *src_left_version;
svn_wc_conflict_version_t *src_right_version;
*pconflict = NULL;
SVN_ERR_ASSERT(reason != SVN_WC_CONFLICT_REASON_NONE);
SVN_ERR_ASSERT(their_relpath != NULL);
if (reason == svn_wc_conflict_reason_added)
{
svn_wc__db_status_t added_status;
left_kind = svn_node_none;
left_revision = SVN_INVALID_REVNUM;
left_repos_relpath = NULL;
SVN_ERR(svn_wc__db_scan_addition(&added_status, NULL,
&added_repos_relpath,
&repos_root_url,
NULL, NULL, NULL, NULL, NULL,
eb->db, local_abspath,
result_pool, scratch_pool));
SVN_ERR_ASSERT(added_status == svn_wc__db_status_added
|| added_status == svn_wc__db_status_copied
|| added_status == svn_wc__db_status_moved_here);
}
else if (reason == svn_wc_conflict_reason_unversioned)
{
left_kind = svn_node_none;
left_revision = SVN_INVALID_REVNUM;
left_repos_relpath = NULL;
repos_root_url = eb->repos_root;
}
else
{
svn_wc__db_kind_t base_kind;
SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_edited
|| reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_replaced
|| reason == svn_wc_conflict_reason_obstructed);
SVN_ERR(svn_wc__db_base_get_info(NULL, &base_kind,
&left_revision,
&left_repos_relpath,
&repos_root_url,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
eb->db,
local_abspath,
result_pool,
scratch_pool));
if (base_kind == svn_wc__db_kind_file
|| base_kind == svn_wc__db_kind_symlink)
left_kind = svn_node_file;
else if (base_kind == svn_wc__db_kind_dir)
left_kind = svn_node_dir;
else
SVN_ERR_MALFUNCTION();
}
SVN_ERR_ASSERT(strcmp(repos_root_url, eb->repos_root) == 0);
if (eb->switch_relpath)
{
right_repos_relpath = their_relpath;
}
else
{
right_repos_relpath = (reason == svn_wc_conflict_reason_added ?
added_repos_relpath : left_repos_relpath);
if (! right_repos_relpath)
right_repos_relpath = their_relpath;
}
SVN_ERR_ASSERT(right_repos_relpath != NULL);
conflict_node_kind = (action == svn_wc_conflict_action_delete ?
left_kind : their_node_kind);
SVN_ERR_ASSERT(conflict_node_kind == svn_node_file
|| conflict_node_kind == svn_node_dir);
if (left_repos_relpath == NULL)
src_left_version = NULL;
else
src_left_version = svn_wc_conflict_version_create(repos_root_url,
left_repos_relpath,
left_revision,
left_kind,
result_pool);
src_right_version = svn_wc_conflict_version_create(repos_root_url,
right_repos_relpath,
*eb->target_revision,
their_node_kind,
result_pool);
*pconflict = svn_wc_conflict_description_create_tree2(
local_abspath, conflict_node_kind,
eb->switch_relpath ?
svn_wc_operation_switch : svn_wc_operation_update,
src_left_version, src_right_version, result_pool);
(*pconflict)->action = action;
(*pconflict)->reason = reason;
return SVN_NO_ERROR;
}
static svn_error_t *
check_tree_conflict(svn_wc_conflict_description2_t **pconflict,
struct edit_baton *eb,
const char *local_abspath,
svn_wc__db_status_t working_status,
svn_wc__db_kind_t working_kind,
svn_boolean_t exists_in_repos,
svn_wc_conflict_action_t action,
svn_node_kind_t their_node_kind,
const char *their_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
svn_boolean_t locally_replaced = FALSE;
svn_boolean_t modified = FALSE;
svn_boolean_t all_mods_are_deletes = FALSE;
SVN_ERR_ASSERT(their_relpath != NULL);
*pconflict = NULL;
switch (working_status)
{
case svn_wc__db_status_added:
case svn_wc__db_status_moved_here:
case svn_wc__db_status_copied:
if (exists_in_repos)
{
svn_wc__db_status_t base_status;
SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (base_status != svn_wc__db_status_not_present)
locally_replaced = TRUE;
}
if (!locally_replaced)
{
SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
reason = svn_wc_conflict_reason_added;
}
else
{
reason = svn_wc_conflict_reason_replaced;
}
break;
case svn_wc__db_status_deleted:
reason = svn_wc_conflict_reason_deleted;
break;
case svn_wc__db_status_incomplete:
case svn_wc__db_status_normal:
if (action == svn_wc_conflict_action_edit)
return SVN_NO_ERROR;
SVN_ERR(node_has_local_mods(&modified, &all_mods_are_deletes,
eb->db, local_abspath,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
if (modified)
{
if (all_mods_are_deletes)
reason = svn_wc_conflict_reason_deleted;
else
reason = svn_wc_conflict_reason_edited;
}
break;
case svn_wc__db_status_server_excluded:
case svn_wc__db_status_excluded:
case svn_wc__db_status_not_present:
return SVN_NO_ERROR;
case svn_wc__db_status_base_deleted:
SVN_ERR_MALFUNCTION();
break;
}
if (reason == SVN_WC_CONFLICT_REASON_NONE)
return SVN_NO_ERROR;
if (reason == svn_wc_conflict_reason_edited
|| reason == svn_wc_conflict_reason_deleted
|| reason == svn_wc_conflict_reason_replaced)
{
if (action != svn_wc_conflict_action_edit
&& action != svn_wc_conflict_action_delete
&& action != svn_wc_conflict_action_replace)
return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
_("Unexpected attempt to add a node at path '%s'"),
svn_dirent_local_style(local_abspath, scratch_pool));
}
else if (reason == svn_wc_conflict_reason_added)
{
if (action != svn_wc_conflict_action_add)
return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
_("Unexpected attempt to edit, delete, or replace "
"a node at path '%s'"),
svn_dirent_local_style(local_abspath, scratch_pool));
}
return svn_error_trace(create_tree_conflict(pconflict, eb, local_abspath,
reason, action, their_node_kind,
their_relpath,
result_pool, scratch_pool));
}
static svn_error_t *
already_in_a_tree_conflict(svn_boolean_t *conflicted,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
const char *ancestor_abspath = local_abspath;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
*conflicted = FALSE;
while (TRUE)
{
svn_boolean_t is_wc_root, has_conflict;
svn_pool_clear(iterpool);
SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
&has_conflict, NULL, NULL, NULL,
NULL, NULL, NULL,
db, ancestor_abspath, iterpool, iterpool));
if (has_conflict)
{
const svn_wc_conflict_description2_t *conflict;
SVN_ERR(svn_wc__db_op_read_tree_conflict(&conflict, db,
ancestor_abspath,
iterpool, iterpool));
if (conflict != NULL)
{
*conflicted = TRUE;
break;
}
}
SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
iterpool));
if (is_wc_root)
break;
ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
node_already_conflicted(svn_boolean_t *conflicted,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
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));
*conflicted = (text_conflicted || prop_conflicted || tree_conflicted);
return SVN_NO_ERROR;
}
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t revision,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
const char *base = svn_relpath_basename(path, NULL);
const char *local_abspath;
const char *repos_relpath;
svn_wc__db_kind_t kind, base_kind;
svn_boolean_t conflicted;
svn_boolean_t have_base;
svn_boolean_t have_work;
svn_wc_conflict_description2_t *tree_conflict = NULL;
svn_skel_t *work_item = NULL;
svn_wc__db_status_t status;
svn_wc__db_status_t base_status;
apr_pool_t *scratch_pool;
svn_boolean_t deleting_target;
if (pb->skip_this)
return SVN_NO_ERROR;
scratch_pool = svn_pool_create(pb->pool);
SVN_ERR(mark_directory_edited(pb, scratch_pool));
SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
scratch_pool));
deleting_target = (strcmp(local_abspath, eb->target_abspath) == 0);
{
svn_boolean_t is_root;
SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
scratch_pool));
if (is_root)
{
remember_skipped_tree(eb, local_abspath, pool);
do_notification(eb, local_abspath, svn_node_unknown,
svn_wc_notify_update_skip_obstruction, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, &repos_relpath, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
NULL, NULL, NULL,
&have_base, NULL, &have_work,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (!have_work)
{
base_status = status;
base_kind = kind;
}
else
SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
&repos_relpath,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool));
if (conflicted)
SVN_ERR(node_already_conflicted(&conflicted, eb->db, local_abspath,
scratch_pool));
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
do_notification(eb, local_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted,
scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
if (base_status == svn_wc__db_status_not_present
|| base_status == svn_wc__db_status_excluded
|| base_status == svn_wc__db_status_server_excluded)
{
SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, scratch_pool));
if (deleting_target)
eb->target_deleted = TRUE;
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
if (!pb->shadowed)
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
status, kind, TRUE,
svn_wc_conflict_action_delete, svn_node_none,
repos_relpath, pb->pool, scratch_pool));
}
if (tree_conflict != NULL)
{
if (!pb->deletion_conflicts)
pb->deletion_conflicts = apr_hash_make(pb->pool);
apr_hash_set(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
APR_HASH_KEY_STRING, tree_conflict);
SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
local_abspath,
tree_conflict,
scratch_pool));
do_notification(eb, local_abspath, svn_node_unknown,
svn_wc_notify_tree_conflict, scratch_pool);
if (tree_conflict->reason == svn_wc_conflict_reason_edited)
{
SVN_ERR(svn_wc__db_temp_op_make_copy(eb->db, local_abspath,
scratch_pool));
}
else if (tree_conflict->reason == svn_wc_conflict_reason_deleted
|| tree_conflict->reason == svn_wc_conflict_reason_replaced)
{
}
else
SVN_ERR_MALFUNCTION();
}
if (! deleting_target)
{
SVN_ERR(svn_wc__wq_build_base_remove(&work_item,
eb->db, local_abspath,
SVN_INVALID_REVNUM,
svn_wc__db_kind_unknown,
scratch_pool, scratch_pool));
}
else
{
SVN_ERR(svn_wc__wq_build_base_remove(&work_item,
eb->db, local_abspath,
*eb->target_revision,
base_kind,
scratch_pool, scratch_pool));
eb->target_deleted = TRUE;
}
SVN_ERR(svn_wc__db_wq_add(eb->db, pb->local_abspath, work_item,
scratch_pool));
SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
if (tree_conflict == NULL)
{
svn_wc_notify_action_t action = svn_wc_notify_update_delete;
svn_node_kind_t node_kind;
if (pb->shadowed)
action = svn_wc_notify_update_shadowed_delete;
if (kind == svn_wc__db_kind_dir)
node_kind = svn_node_dir;
else
node_kind = svn_node_file;
do_notification(eb, local_abspath, node_kind, action, scratch_pool);
}
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_rev,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
struct dir_baton *db;
svn_node_kind_t kind;
svn_wc__db_status_t status;
svn_wc__db_kind_t wc_kind;
svn_boolean_t conflicted;
svn_boolean_t versioned_locally_and_present;
svn_wc_conflict_description2_t *tree_conflict = NULL;
svn_error_t *err;
SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
*child_baton = db;
if (db->skip_this)
return SVN_NO_ERROR;
SVN_ERR(mark_directory_edited(db, pool));
if (strcmp(eb->target_abspath, db->local_abspath) == 0)
{
db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
? svn_depth_infinity : eb->requested_depth;
}
else if (eb->requested_depth == svn_depth_immediates
|| (eb->requested_depth == svn_depth_unknown
&& pb->ambient_depth == svn_depth_immediates))
{
db->ambient_depth = svn_depth_empty;
}
else
{
db->ambient_depth = svn_depth_infinity;
}
if (svn_wc_is_adm_dir(db->name, pool))
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to add directory '%s': object of the same name as the "
"administrative directory"),
svn_dirent_local_style(db->local_abspath, pool));
SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
eb->db, db->local_abspath, db->pool, db->pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
wc_kind = svn_wc__db_kind_unknown;
status = svn_wc__db_status_normal;
conflicted = FALSE;
versioned_locally_and_present = FALSE;
}
else if (wc_kind == svn_wc__db_kind_dir
&& status == svn_wc__db_status_normal)
{
SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
db->new_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
svn_wc__db_kind_file,
NULL, NULL,
pool));
remember_skipped_tree(eb, db->local_abspath, pool);
db->skip_this = TRUE;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_update_skip_obstruction, pool);
return SVN_NO_ERROR;
}
else if (status == svn_wc__db_status_normal
&& (wc_kind == svn_wc__db_kind_file
|| wc_kind == svn_wc__db_kind_symlink))
{
remember_skipped_tree(eb, db->local_abspath, pool);
db->skip_this = TRUE;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_file,
svn_wc_notify_update_skip_obstruction, pool);
return SVN_NO_ERROR;
}
else if (wc_kind == svn_wc__db_kind_unknown)
versioned_locally_and_present = FALSE;
else
versioned_locally_and_present = IS_NODE_PRESENT(status);
if (conflicted)
{
if (pb->deletion_conflicts)
tree_conflict = apr_hash_get(pb->deletion_conflicts, db->name,
APR_HASH_KEY_STRING);
if (tree_conflict)
{
tree_conflict->action = svn_wc_conflict_action_replace;
SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, db->local_abspath,
tree_conflict, pool));
tree_conflict = NULL;
db->shadowed = TRUE;
conflicted = FALSE;
}
else
SVN_ERR(node_already_conflicted(&conflicted, eb->db,
db->local_abspath, pool));
}
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
db->new_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
svn_wc__db_kind_dir,
NULL, NULL,
pool));
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_skip_conflicted, pool);
return SVN_NO_ERROR;
}
if (db->shadowed)
{
}
else if (versioned_locally_and_present)
{
svn_boolean_t local_is_non_dir;
svn_wc__db_status_t add_status = svn_wc__db_status_normal;
if (status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
eb->db, db->local_abspath,
pool, pool));
local_is_non_dir = (wc_kind != svn_wc__db_kind_dir
&& status != svn_wc__db_status_deleted);
if (!eb->adds_as_modification
|| local_is_non_dir
|| add_status != svn_wc__db_status_added)
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb,
db->local_abspath,
status, wc_kind, FALSE,
svn_wc_conflict_action_add,
svn_node_dir, db->new_relpath,
pool, pool));
}
if (tree_conflict == NULL)
db->add_existed = TRUE;
else
db->shadowed = TRUE;
}
else if (kind != svn_node_none)
{
db->obstruction_found = TRUE;
if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
{
db->shadowed = TRUE;
SVN_ERR(create_tree_conflict(&tree_conflict, eb,
db->local_abspath,
svn_wc_conflict_reason_unversioned,
svn_wc_conflict_action_add,
svn_node_dir,
db->new_relpath, pool, pool));
SVN_ERR_ASSERT(tree_conflict != NULL);
}
}
SVN_ERR(svn_wc__db_temp_op_set_new_dir_to_incomplete(eb->db,
db->local_abspath,
db->new_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
db->ambient_depth,
pool));
if (!db->shadowed)
SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
if (!db->shadowed && status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_temp_op_remove_working(eb->db, db->local_abspath, pool));
if (db->shadowed && db->obstruction_found)
{
SVN_ERR(svn_wc__db_op_delete(eb->db, db->local_abspath,
NULL, NULL ,
eb->cancel_func, eb->cancel_baton,
pool));
}
if (tree_conflict != NULL)
{
SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, db->local_abspath,
tree_conflict, pool));
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_tree_conflict, pool);
}
if (eb->notify_func && !db->already_notified && !db->add_existed)
{
svn_wc_notify_action_t action;
if (db->shadowed)
action = svn_wc_notify_update_shadowed_add;
else if (db->obstruction_found)
action = svn_wc_notify_exists;
else
action = svn_wc_notify_update_add;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_dir, action, 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 *db, *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
svn_boolean_t have_work;
svn_boolean_t conflicted;
svn_wc_conflict_description2_t *tree_conflict = NULL;
svn_wc__db_status_t status, base_status;
svn_wc__db_kind_t wc_kind;
SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
*child_baton = db;
if (db->skip_this)
return SVN_NO_ERROR;
{
svn_boolean_t is_root;
SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
pool));
if (is_root)
{
remember_skipped_tree(eb, db->local_abspath, pool);
db->skip_this = TRUE;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_update_skip_obstruction, pool);
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision, NULL,
NULL, NULL, &db->changed_rev, &db->changed_date,
&db->changed_author, &db->ambient_depth,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, NULL,
NULL, NULL, &have_work,
eb->db, db->local_abspath,
db->pool, pool));
if (!have_work)
base_status = status;
else
SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
NULL, NULL, NULL, &db->changed_rev,
&db->changed_date, &db->changed_author,
&db->ambient_depth, NULL, NULL, NULL,
NULL, NULL,
eb->db, db->local_abspath,
db->pool, pool));
db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
if (conflicted)
SVN_ERR(node_already_conflicted(&conflicted, eb->db,
db->local_abspath, pool));
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
do_notification(eb, db->local_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted, pool);
return SVN_NO_ERROR;
}
if (!db->shadowed)
SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
status, wc_kind, TRUE,
svn_wc_conflict_action_edit, svn_node_dir,
db->new_relpath, db->pool, pool));
if (tree_conflict != NULL)
{
db->edit_conflict = tree_conflict;
SVN_ERR_ASSERT(
tree_conflict->reason == svn_wc_conflict_reason_deleted ||
tree_conflict->reason == svn_wc_conflict_reason_replaced);
db->shadowed = TRUE;
}
SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
db->new_relpath,
*eb->target_revision,
pool));
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)
{
svn_prop_t *propchange;
struct dir_baton *db = dir_baton;
if (db->skip_this)
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;
if (!db->edited && svn_property_kind(NULL, name) == svn_prop_regular_kind)
SVN_ERR(mark_directory_edited(db, pool));
return SVN_NO_ERROR;
}
static const svn_prop_t *
externals_prop_changed(const apr_array_header_t *propchanges)
{
int i;
for (i = 0; i < propchanges->nelts; i++)
{
const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
return p;
}
return NULL;
}
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
struct dir_baton *db = dir_baton;
struct edit_baton *eb = db->edit_baton;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
apr_array_header_t *entry_prop_changes;
apr_array_header_t *dav_prop_changes;
apr_array_header_t *regular_prop_changes;
apr_hash_t *base_props;
apr_hash_t *actual_props;
apr_hash_t *new_base_props = NULL;
apr_hash_t *new_actual_props = NULL;
svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
apr_time_t new_changed_date = 0;
const char *new_changed_author = NULL;
apr_pool_t *scratch_pool = db->pool;
svn_skel_t *all_work_items = NULL;
if (db->skip_this)
{
SVN_ERR(maybe_release_dir_info(db->bump_info));
return SVN_NO_ERROR;
}
SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
&dav_prop_changes, ®ular_prop_changes, pool));
if ((!db->adding_dir || db->add_existed)
&& !db->shadowed)
{
SVN_ERR(svn_wc__get_actual_props(&actual_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
}
else
actual_props = apr_hash_make(pool);
if (db->add_existed)
{
SVN_ERR(svn_wc__get_pristine_props(&base_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
}
else if (!db->adding_dir)
{
SVN_ERR(svn_wc__db_base_get_props(&base_props,
eb->db, db->local_abspath,
scratch_pool, scratch_pool));
}
else
base_props = apr_hash_make(pool);
if (db->was_incomplete)
{
int i;
apr_hash_t *props_to_delete;
apr_hash_index_t *hi;
props_to_delete = apr_hash_copy(pool, base_props);
for (i = 0; i < regular_prop_changes->nelts; i++)
{
const svn_prop_t *prop;
prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
apr_hash_set(props_to_delete, prop->name,
APR_HASH_KEY_STRING, NULL);
}
for (hi = apr_hash_first(pool, props_to_delete);
hi != NULL;
hi = apr_hash_next(hi))
{
const char *propname = svn__apr_hash_index_key(hi);
svn_prop_t *prop = apr_array_push(regular_prop_changes);
prop->name = propname;
prop->value = NULL;
}
}
if (regular_prop_changes->nelts)
{
svn_skel_t *work_item;
if (eb->external_func)
{
const svn_prop_t *change
= externals_prop_changed(regular_prop_changes);
if (change)
{
const svn_string_t *new_val_s = change->value;
const svn_string_t *old_val_s;
old_val_s = apr_hash_get(base_props, SVN_PROP_EXTERNALS,
APR_HASH_KEY_STRING);
if ((new_val_s == NULL) && (old_val_s == NULL))
;
else if (new_val_s && old_val_s
&& (svn_string_compare(old_val_s, new_val_s)))
;
else if (old_val_s || new_val_s)
{
SVN_ERR((eb->external_func)(
eb->external_baton,
db->local_abspath,
old_val_s,
new_val_s,
db->ambient_depth,
db->pool));
}
}
}
if (db->shadowed)
{
if (db->adding_dir)
actual_props = apr_hash_make(scratch_pool);
else
actual_props = base_props;
}
SVN_ERR_W(svn_wc__merge_props(&work_item,
&prop_state,
&new_base_props,
&new_actual_props,
eb->db,
db->local_abspath,
svn_wc__db_kind_dir,
NULL,
NULL,
NULL ,
base_props,
actual_props,
regular_prop_changes,
TRUE ,
FALSE ,
eb->conflict_func,
eb->conflict_baton,
eb->cancel_func,
eb->cancel_baton,
db->pool,
scratch_pool),
_("Couldn't do property merge"));
SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
&new_changed_author, entry_prop_changes,
scratch_pool, scratch_pool));
{
apr_hash_t *new_children = apr_hash_get(eb->dir_dirents, db->new_relpath,
APR_HASH_KEY_STRING);
if (new_children != NULL)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, new_children);
hi;
hi = apr_hash_next(hi))
{
const char *child_name;
const char *child_abspath;
const char *child_relpath;
const svn_dirent_t *dirent;
svn_wc__db_status_t status;
svn_wc__db_kind_t child_kind;
svn_error_t *err;
svn_pool_clear(iterpool);
child_name = svn__apr_hash_index_key(hi);
child_abspath = svn_dirent_join(db->local_abspath, child_name,
iterpool);
dirent = svn__apr_hash_index_val(hi);
child_kind = (dirent->kind == svn_node_dir)
? svn_wc__db_kind_dir
: svn_wc__db_kind_file;
if (db->ambient_depth < svn_depth_immediates
&& child_kind == svn_wc__db_kind_dir)
continue;
err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
eb->db, child_abspath,
iterpool, iterpool);
if (!err)
{
svn_boolean_t is_wcroot;
SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
iterpool));
if (!is_wcroot)
continue;
}
else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
child_relpath = svn_relpath_join(db->new_relpath, child_name,
iterpool);
SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
child_abspath,
child_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
child_kind,
NULL, NULL,
iterpool));
}
svn_pool_destroy(iterpool);
}
}
if (apr_hash_count(db->not_present_files))
{
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, db->not_present_files);
hi;
hi = apr_hash_next(hi))
{
const char *child = svn__apr_hash_index_key(hi);
const char *child_abspath, *child_relpath;
svn_pool_clear(iterpool);
child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
child_relpath = svn_dirent_join(db->new_relpath, child, iterpool);
SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
child_abspath,
child_relpath,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
svn_wc__db_kind_file,
NULL, NULL,
iterpool));
}
svn_pool_destroy(iterpool);
}
if (db->parent_baton == NULL
&& *eb->target_basename != '\0')
{
SVN_ERR_ASSERT(db->propchanges->nelts == 0);
}
else
{
apr_hash_t *props;
if (SVN_IS_VALID_REVNUM(new_changed_rev))
db->changed_rev = new_changed_rev;
if (new_changed_date != 0)
db->changed_date = new_changed_date;
if (new_changed_author != NULL)
db->changed_author = new_changed_author;
if (db->ambient_depth == svn_depth_unknown)
db->ambient_depth = svn_depth_infinity;
if (eb->depth_is_sticky
&& db->ambient_depth != eb->requested_depth)
{
if (eb->requested_depth == svn_depth_infinity
|| (strcmp(db->local_abspath, eb->target_abspath) == 0
&& eb->requested_depth > db->ambient_depth))
{
db->ambient_depth = eb->requested_depth;
}
}
props = new_base_props;
if (props == NULL)
props = base_props;
SVN_ERR(svn_wc__db_base_add_directory(
eb->db, db->local_abspath,
eb->wcroot_abspath,
db->new_relpath,
eb->repos_root, eb->repos_uuid,
*eb->target_revision,
props,
db->changed_rev, db->changed_date, db->changed_author,
NULL ,
db->ambient_depth,
(dav_prop_changes->nelts > 0)
? svn_prop_array_to_hash(dav_prop_changes, pool)
: NULL,
NULL ,
(! db->shadowed) && new_base_props != NULL,
new_actual_props,
all_work_items,
scratch_pool));
}
SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
if (!db->already_notified && eb->notify_func && db->edited)
{
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
if (db->shadowed)
action = svn_wc_notify_update_shadowed_update;
else if (db->obstruction_found || db->add_existed)
action = svn_wc_notify_exists;
else
action = svn_wc_notify_update_update;
notify = svn_wc_create_notify(db->local_abspath, action, pool);
notify->kind = svn_node_dir;
notify->prop_state = prop_state;
notify->revision = *eb->target_revision;
notify->old_revision = db->old_revision;
eb->notify_func(eb->notify_baton, notify, scratch_pool);
}
SVN_ERR(maybe_release_dir_info(db->bump_info));
return SVN_NO_ERROR;
}
static svn_error_t *
absent_node(const char *path,
svn_wc__db_kind_t absent_kind,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
apr_pool_t *scratch_pool = svn_pool_create(pool);
const char *name = svn_dirent_basename(path, NULL);
const char *local_abspath;
svn_error_t *err;
svn_wc__db_status_t status;
svn_wc__db_kind_t kind;
if (pb->skip_this)
return SVN_NO_ERROR;
SVN_ERR(mark_directory_edited(pb, scratch_pool));
local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
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,
eb->db, local_abspath,
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);
status = svn_wc__db_status_not_present;
kind = svn_wc__db_kind_unknown;
}
if (status == svn_wc__db_status_normal
&& kind == svn_wc__db_kind_dir)
{
}
else if (status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded)
{
}
else
{
SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to mark '%s' absent: item of the same name is already "
"scheduled for addition"),
svn_dirent_local_style(local_abspath, pool));
}
{
const char *repos_relpath;
repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool);
SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
repos_relpath, eb->repos_root,
eb->repos_uuid,
*(eb->target_revision),
absent_kind,
svn_wc__db_status_server_excluded,
NULL, NULL,
scratch_pool));
}
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
absent_file(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
return absent_node(path, svn_wc__db_kind_file, parent_baton, pool);
}
static svn_error_t *
absent_directory(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
return absent_node(path, svn_wc__db_kind_dir, parent_baton, pool);
}
static svn_error_t *
add_file(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
apr_pool_t *pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
struct file_baton *fb;
svn_node_kind_t kind = svn_node_none;
svn_wc__db_kind_t wc_kind = svn_wc__db_kind_unknown;
svn_wc__db_status_t status = svn_wc__db_status_normal;
apr_pool_t *scratch_pool;
svn_boolean_t conflicted = FALSE;
svn_boolean_t versioned_locally_and_present = FALSE;
svn_wc_conflict_description2_t *tree_conflict = NULL;
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
*file_baton = fb;
if (fb->skip_this)
return SVN_NO_ERROR;
SVN_ERR(mark_file_edited(fb, pool));
scratch_pool = svn_pool_create(pool);
if (svn_wc_is_adm_dir(fb->name, pool))
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to add file '%s': object of the same name as the "
"administrative directory"),
svn_dirent_local_style(fb->local_abspath, pool));
if (!eb->clean_checkout)
{
SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
eb->db, fb->local_abspath,
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);
wc_kind = svn_wc__db_kind_unknown;
conflicted = FALSE;
versioned_locally_and_present = FALSE;
}
else if (wc_kind == svn_wc__db_kind_dir
&& status == svn_wc__db_status_normal)
{
apr_hash_set(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
APR_HASH_KEY_STRING, (void*)1);
remember_skipped_tree(eb, fb->local_abspath, pool);
fb->skip_this = TRUE;
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_update_skip_obstruction, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
else if (status == svn_wc__db_status_normal
&& (wc_kind == svn_wc__db_kind_file
|| wc_kind == svn_wc__db_kind_symlink))
{
remember_skipped_tree(eb, fb->local_abspath, pool);
fb->skip_this = TRUE;
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_update_skip_obstruction, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
else if (wc_kind == svn_wc__db_kind_unknown)
versioned_locally_and_present = FALSE;
else
versioned_locally_and_present = IS_NODE_PRESENT(status);
if (conflicted)
if (conflicted)
{
if (pb->deletion_conflicts)
tree_conflict = apr_hash_get(pb->deletion_conflicts, fb->name,
APR_HASH_KEY_STRING);
if (tree_conflict)
{
tree_conflict->action = svn_wc_conflict_action_replace;
SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, fb->local_abspath,
tree_conflict, pool));
tree_conflict = NULL;
fb->shadowed = TRUE;
conflicted = FALSE;
}
else
SVN_ERR(node_already_conflicted(&conflicted, eb->db,
fb->local_abspath, pool));
}
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
apr_hash_set(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
APR_HASH_KEY_STRING, (void*)1);
do_notification(eb, fb->local_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
if (fb->shadowed)
{
}
else if (versioned_locally_and_present)
{
svn_boolean_t local_is_file;
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));
local_is_file = (wc_kind == svn_wc__db_kind_file
|| wc_kind == svn_wc__db_kind_symlink);
if (!eb->adds_as_modification
|| !local_is_file
|| status != svn_wc__db_status_added)
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb,
fb->local_abspath,
status, wc_kind, FALSE,
svn_wc_conflict_action_add,
svn_node_file, fb->new_relpath,
scratch_pool, scratch_pool));
}
if (tree_conflict == NULL)
fb->add_existed = TRUE;
else
fb->shadowed = TRUE;
}
else if (kind != svn_node_none)
{
fb->obstruction_found = TRUE;
if (! (kind == svn_node_file && eb->allow_unver_obstructions))
{
fb->shadowed = TRUE;
SVN_ERR(create_tree_conflict(&tree_conflict, eb,
fb->local_abspath,
svn_wc_conflict_reason_unversioned,
svn_wc_conflict_action_add,
svn_node_file,
fb->new_relpath,
scratch_pool, scratch_pool));
SVN_ERR_ASSERT(tree_conflict != NULL);
}
}
if (pb->parent_baton
|| *eb->target_basename == '\0'
|| (strcmp(fb->local_abspath, eb->target_abspath) != 0))
{
apr_hash_set(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
APR_HASH_KEY_STRING, (void*)1);
}
if (tree_conflict != NULL)
{
SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
fb->local_abspath,
tree_conflict,
scratch_pool));
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_tree_conflict, scratch_pool);
}
svn_pool_destroy(scratch_pool);
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 edit_baton *eb = pb->edit_baton;
struct file_baton *fb;
svn_boolean_t conflicted;
svn_boolean_t have_work;
svn_wc__db_status_t status;
svn_wc__db_kind_t wc_kind;
svn_wc_conflict_description2_t *tree_conflict = NULL;
apr_pool_t *scratch_pool = svn_pool_create(pool);
SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
*file_baton = fb;
if (fb->skip_this)
return SVN_NO_ERROR;
{
svn_boolean_t is_root;
SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
pool));
if (is_root)
{
remember_skipped_tree(eb, fb->local_abspath, pool);
fb->skip_this = TRUE;
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_update_skip_obstruction, pool);
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision, NULL,
NULL, NULL, &fb->changed_rev, &fb->changed_date,
&fb->changed_author, NULL,
&fb->original_checksum, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
&conflicted, NULL, NULL, &fb->local_prop_mods,
NULL, NULL, &have_work,
eb->db, fb->local_abspath,
fb->pool, scratch_pool));
if (have_work)
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
NULL, NULL, NULL, &fb->changed_rev,
&fb->changed_date, &fb->changed_author,
NULL, &fb->original_checksum, NULL, NULL,
NULL, NULL,
eb->db, fb->local_abspath,
fb->pool, scratch_pool));
if (conflicted)
SVN_ERR(node_already_conflicted(&conflicted, eb->db,
fb->local_abspath, pool));
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted, scratch_pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
fb->shadowed = pb->shadowed;
if (!fb->shadowed)
SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
status, wc_kind, TRUE,
svn_wc_conflict_action_edit, svn_node_file,
fb->new_relpath, fb->pool, scratch_pool));
if (tree_conflict != NULL)
{
fb->edit_conflict = tree_conflict;
SVN_ERR_ASSERT(
tree_conflict->reason == svn_wc_conflict_reason_deleted ||
tree_conflict->reason == svn_wc_conflict_reason_replaced);
fb->shadowed = TRUE;
}
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
apply_textdelta(void *file_baton,
const char *expected_checksum,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
struct file_baton *fb = file_baton;
apr_pool_t *handler_pool = svn_pool_create(fb->pool);
struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
struct edit_baton *eb = fb->edit_baton;
svn_error_t *err;
const svn_checksum_t *recorded_base_checksum;
svn_checksum_t *expected_base_checksum;
svn_stream_t *source;
svn_stream_t *target;
if (fb->skip_this)
{
*handler = svn_delta_noop_window_handler;
*handler_baton = NULL;
return SVN_NO_ERROR;
}
SVN_ERR(mark_file_edited(fb, pool));
SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
expected_checksum, pool));
recorded_base_checksum = fb->original_checksum;
if (recorded_base_checksum
&& expected_base_checksum
&& recorded_base_checksum->kind != svn_checksum_md5)
SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
eb->db, eb->wcroot_abspath,
recorded_base_checksum, pool, pool));
if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
_("Checksum mismatch for '%s':\n"
" expected: %s\n"
" recorded: %s\n"),
svn_dirent_local_style(fb->local_abspath, pool),
svn_checksum_to_cstring_display(expected_base_checksum,
pool),
svn_checksum_to_cstring_display(recorded_base_checksum,
pool));
if (! fb->adding_file)
{
SVN_ERR_ASSERT(!fb->original_checksum
|| fb->original_checksum->kind == svn_checksum_sha1);
SVN_ERR(svn_wc__db_pristine_read(&source, NULL, fb->edit_baton->db,
fb->local_abspath,
fb->original_checksum,
handler_pool, handler_pool));
}
else
{
source = svn_stream_empty(handler_pool);
}
if (!recorded_base_checksum)
recorded_base_checksum = expected_base_checksum;
if (recorded_base_checksum)
{
hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
handler_pool);
source = svn_stream_checksummed2(source,
&hb->actual_source_checksum,
NULL, recorded_base_checksum->kind,
TRUE, handler_pool);
hb->source_checksum_stream = source;
}
err = svn_wc__open_writable_base(&target, &hb->new_text_base_tmp_abspath,
NULL, &hb->new_text_base_sha1_checksum,
fb->edit_baton->db, eb->wcroot_abspath,
handler_pool, pool);
if (err)
{
svn_pool_destroy(handler_pool);
return svn_error_trace(err);
}
svn_txdelta_apply(source, target,
hb->new_text_base_md5_digest,
hb->new_text_base_tmp_abspath ,
handler_pool,
&hb->apply_handler, &hb->apply_baton);
hb->pool = handler_pool;
hb->fb = fb;
*handler_baton = hb;
*handler = 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 *scratch_pool)
{
struct file_baton *fb = file_baton;
svn_prop_t *propchange;
if (fb->skip_this)
return SVN_NO_ERROR;
propchange = apr_array_push(fb->propchanges);
propchange->name = apr_pstrdup(fb->pool, name);
propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
if (!fb->edited && svn_property_kind(NULL, name) == svn_prop_regular_kind)
SVN_ERR(mark_file_edited(fb, scratch_pool));
if (! fb->shadowed
&& strcmp(name, SVN_PROP_SPECIAL) == 0)
{
struct edit_baton *eb = fb->edit_baton;
svn_boolean_t modified = FALSE;
svn_boolean_t becomes_symlink;
svn_boolean_t was_symlink;
becomes_symlink = (value != NULL);
if (fb->adding_file)
was_symlink = becomes_symlink;
else
{
apr_hash_t *props;
SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
fb->local_abspath,
scratch_pool, scratch_pool));
was_symlink = ((props
&& apr_hash_get(props, SVN_PROP_SPECIAL,
APR_HASH_KEY_STRING) != NULL)
? svn_tristate_true
: svn_tristate_false);
}
if (was_symlink != becomes_symlink)
{
if (fb->local_prop_mods)
modified = TRUE;
else
SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
fb->local_abspath,
FALSE, scratch_pool));
}
if (modified)
{
svn_wc_conflict_description2_t *tree_conflict = NULL;
SVN_ERR(svn_wc__db_temp_op_make_copy(eb->db, fb->local_abspath,
scratch_pool));
SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
svn_wc__db_status_added,
svn_wc__db_kind_file, TRUE,
svn_wc_conflict_action_edit,
svn_node_file, fb->new_relpath,
scratch_pool, scratch_pool));
SVN_ERR_ASSERT(tree_conflict != NULL);
SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
fb->local_abspath,
tree_conflict,
scratch_pool));
fb->edit_conflict = tree_conflict;
do_notification(eb, fb->local_abspath, svn_node_file,
svn_wc_notify_tree_conflict, scratch_pool);
fb->shadowed = TRUE;
fb->add_existed = FALSE;
fb->already_notified = TRUE;
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__perform_file_merge(svn_skel_t **work_items,
enum svn_wc_merge_outcome_t *merge_outcome,
svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const svn_checksum_t *new_checksum,
const svn_checksum_t *original_checksum,
apr_hash_t *actual_props,
const apr_array_header_t *ext_patterns,
svn_revnum_t old_revision,
svn_revnum_t target_revision,
const apr_array_header_t *propchanges,
const char *diff3_cmd,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *oldrev_str, *newrev_str, *mine_str;
const char *merge_left;
svn_boolean_t delete_left = FALSE;
const char *path_ext = "";
const char *new_text_base_tmp_abspath;
svn_skel_t *work_item;
*work_items = NULL;
SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath,
db, wri_abspath, new_checksum,
scratch_pool, scratch_pool));
if (ext_patterns && ext_patterns->nelts)
{
svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
path_ext = "";
}
if (!SVN_IS_VALID_REVNUM(old_revision))
old_revision = 0;
oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
old_revision,
*path_ext ? "." : "",
*path_ext ? path_ext : "");
newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
target_revision,
*path_ext ? "." : "",
*path_ext ? path_ext : "");
mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
*path_ext ? "." : "",
*path_ext ? path_ext : "");
if (! original_checksum)
{
SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
result_pool, scratch_pool));
delete_left = TRUE;
}
else
SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
original_checksum,
result_pool, scratch_pool));
SVN_ERR(svn_wc__internal_merge(&work_item,
merge_outcome,
db,
merge_left, NULL,
new_text_base_tmp_abspath, NULL,
local_abspath,
wri_abspath,
oldrev_str, newrev_str, mine_str,
actual_props,
FALSE ,
diff3_cmd, NULL, propchanges,
conflict_func, conflict_baton,
cancel_func, cancel_baton,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
if (delete_left)
{
SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, merge_left,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
merge_file(svn_skel_t **work_items,
svn_boolean_t *install_pristine,
const char **install_from,
svn_wc_notify_state_t *content_state,
struct file_baton *fb,
apr_hash_t *actual_props,
apr_time_t last_changed_date,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb = fb->edit_baton;
struct dir_baton *pb = fb->dir_baton;
svn_boolean_t is_locally_modified;
enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
svn_skel_t *work_item;
SVN_ERR_ASSERT(! fb->shadowed && !fb->obstruction_found);
*work_items = NULL;
*install_pristine = FALSE;
*install_from = NULL;
if (fb->adding_file && !fb->add_existed)
{
is_locally_modified = FALSE;
}
else
{
SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
eb->db, fb->local_abspath,
FALSE ,
scratch_pool));
}
if (! is_locally_modified
&& fb->new_text_base_sha1_checksum)
{
*install_pristine = TRUE;
}
else if (fb->new_text_base_sha1_checksum)
{
SVN_ERR(svn_wc__perform_file_merge(work_items,
&merge_outcome,
eb->db,
fb->local_abspath,
pb->local_abspath,
fb->new_text_base_sha1_checksum,
fb->add_existed
? NULL
: fb->original_checksum,
actual_props,
eb->ext_patterns,
fb->old_revision,
*eb->target_revision,
fb->propchanges,
eb->diff3_cmd,
eb->conflict_func, eb->conflict_baton,
eb->cancel_func, eb->cancel_baton,
result_pool, scratch_pool));
}
else
{
apr_hash_t *keywords;
svn_boolean_t magic_props_changed;
magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
&keywords,
NULL,
eb->db, fb->local_abspath,
actual_props, TRUE,
scratch_pool, scratch_pool));
if (magic_props_changed || keywords)
{
if (is_locally_modified)
{
const char *tmptext;
SVN_ERR(svn_wc__internal_translated_file(
&tmptext, fb->local_abspath, eb->db, fb->local_abspath,
SVN_WC_TRANSLATE_TO_NF
| SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
eb->cancel_func, eb->cancel_baton,
result_pool, scratch_pool));
*install_pristine = TRUE;
*install_from = tmptext;
}
else
{
*install_pristine = TRUE;
}
}
}
if (!*install_pristine
&& !is_locally_modified)
{
apr_time_t set_date = 0;
if (eb->use_commit_times && last_changed_date != 0)
{
set_date = last_changed_date;
}
SVN_ERR(svn_wc__wq_build_record_fileinfo(&work_item,
eb->db, fb->local_abspath,
set_date,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
if (merge_outcome == svn_wc_merge_conflict)
*content_state = svn_wc_notify_state_conflicted;
else if (fb->new_text_base_sha1_checksum)
{
if (is_locally_modified)
*content_state = svn_wc_notify_state_merged;
else
*content_state = svn_wc_notify_state_changed;
}
else
*content_state = svn_wc_notify_state_unchanged;
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->edit_baton;
svn_wc_notify_state_t content_state, prop_state;
svn_wc_notify_lock_state_t lock_state;
svn_checksum_t *expected_md5_checksum = NULL;
apr_hash_t *new_base_props = NULL;
apr_hash_t *new_actual_props = NULL;
apr_array_header_t *entry_prop_changes;
apr_array_header_t *dav_prop_changes;
apr_array_header_t *regular_prop_changes;
apr_hash_t *current_base_props = NULL;
apr_hash_t *current_actual_props = NULL;
apr_hash_t *local_actual_props = NULL;
svn_skel_t *all_work_items = NULL;
svn_skel_t *work_item;
apr_pool_t *scratch_pool = fb->pool;
svn_boolean_t keep_recorded_info = FALSE;
if (fb->skip_this)
{
SVN_ERR(maybe_release_dir_info(fb->bump_info));
svn_pool_destroy(fb->pool);
return SVN_NO_ERROR;
}
if (expected_md5_digest)
SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
expected_md5_digest, scratch_pool));
if (fb->new_text_base_md5_checksum && expected_md5_checksum
&& !svn_checksum_match(expected_md5_checksum,
fb->new_text_base_md5_checksum))
return svn_checksum_mismatch_err(expected_md5_checksum,
fb->new_text_base_md5_checksum, scratch_pool,
_("Checksum mismatch for '%s'"),
svn_dirent_local_style(fb->local_abspath, pool));
SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
&dav_prop_changes, ®ular_prop_changes,
scratch_pool));
{
svn_revnum_t new_changed_rev;
apr_time_t new_changed_date;
const char *new_changed_author;
SVN_ERR(accumulate_last_change(&new_changed_rev,
&new_changed_date,
&new_changed_author,
entry_prop_changes,
scratch_pool, scratch_pool));
if (SVN_IS_VALID_REVNUM(new_changed_rev))
fb->changed_rev = new_changed_rev;
if (new_changed_date != 0)
fb->changed_date = new_changed_date;
if (new_changed_author != NULL)
fb->changed_author = new_changed_author;
}
{
int i;
lock_state = svn_wc_notify_lock_state_unchanged;
for (i = 0; i < entry_prop_changes->nelts; ++i)
{
const svn_prop_t *prop
= &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
{
SVN_ERR_ASSERT(prop->value == NULL);
SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
scratch_pool));
lock_state = svn_wc_notify_lock_state_unlocked;
break;
}
}
}
if ((!fb->adding_file || fb->add_existed)
&& !fb->shadowed)
SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
if (local_actual_props == NULL)
local_actual_props = apr_hash_make(scratch_pool);
if (fb->add_existed)
{
SVN_ERR(svn_wc__get_pristine_props(¤t_base_props,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
current_actual_props = local_actual_props;
}
else if (!fb->adding_file)
{
SVN_ERR(svn_wc__db_base_get_props(¤t_base_props,
eb->db, fb->local_abspath,
scratch_pool, scratch_pool));
current_actual_props = local_actual_props;
}
if (current_base_props == NULL)
current_base_props = apr_hash_make(scratch_pool);
if (current_actual_props == NULL)
current_actual_props = apr_hash_make(scratch_pool);
prop_state = svn_wc_notify_state_unknown;
if (! fb->shadowed)
{
svn_boolean_t install_pristine;
const char *install_from = NULL;
SVN_ERR(svn_wc__merge_props(&work_item,
&prop_state,
&new_base_props,
&new_actual_props,
eb->db,
fb->local_abspath,
svn_wc__db_kind_file,
NULL ,
NULL ,
NULL ,
current_base_props,
current_actual_props,
regular_prop_changes,
TRUE ,
FALSE ,
eb->conflict_func, eb->conflict_baton,
eb->cancel_func, eb->cancel_baton,
scratch_pool,
scratch_pool));
SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
if (!fb->obstruction_found)
{
svn_error_t *err;
err = merge_file(&work_item, &install_pristine, &install_from,
&content_state, fb, current_actual_props,
fb->changed_date, scratch_pool, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
{
if (eb->notify_func)
{
svn_wc_notify_t *notify =svn_wc_create_notify(
fb->local_abspath,
svn_wc_notify_update_skip_access_denied,
scratch_pool);
notify->kind = svn_node_file;
notify->err = err;
eb->notify_func(eb->notify_baton, notify, scratch_pool);
}
svn_error_clear(err);
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
scratch_pool));
fb->skip_this = TRUE;
SVN_ERR(maybe_release_dir_info(fb->bump_info));
svn_pool_destroy(fb->pool);
return SVN_NO_ERROR;
}
else
SVN_ERR(err);
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
else
{
install_pristine = FALSE;
if (fb->new_text_base_sha1_checksum)
content_state = svn_wc_notify_state_changed;
else
content_state = svn_wc_notify_state_unchanged;
}
if (install_pristine)
{
svn_boolean_t record_fileinfo;
record_fileinfo = (install_from == NULL);
SVN_ERR(svn_wc__wq_build_file_install(&work_item,
eb->db,
fb->local_abspath,
install_from,
eb->use_commit_times,
record_fileinfo,
scratch_pool, scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
else if (lock_state == svn_wc_notify_lock_state_unlocked
&& !fb->obstruction_found)
{
SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
fb->local_abspath,
scratch_pool, scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
if (! install_pristine
&& (content_state == svn_wc_notify_state_unchanged))
{
keep_recorded_info = TRUE;
}
if (install_from != NULL
&& strcmp(install_from, fb->local_abspath) != 0)
{
SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
install_from,
scratch_pool, scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
}
}
else
{
apr_hash_t *fake_actual_props;
if (fb->adding_file)
fake_actual_props = apr_hash_make(scratch_pool);
else
fake_actual_props = current_base_props;
SVN_ERR(svn_wc__merge_props(&work_item,
&prop_state,
&new_base_props,
&new_actual_props,
eb->db,
fb->local_abspath,
svn_wc__db_kind_file,
NULL ,
NULL ,
NULL ,
current_base_props ,
fake_actual_props ,
regular_prop_changes,
TRUE ,
FALSE ,
NULL, NULL,
eb->cancel_func, eb->cancel_baton,
scratch_pool,
scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
if (fb->new_text_base_sha1_checksum)
content_state = svn_wc_notify_state_changed;
else
content_state = svn_wc_notify_state_unchanged;
}
{
const svn_checksum_t *new_checksum = fb->new_text_base_sha1_checksum;
if (new_checksum == NULL)
new_checksum = fb->original_checksum;
SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
eb->wcroot_abspath,
fb->new_relpath,
eb->repos_root, eb->repos_uuid,
*eb->target_revision,
new_base_props,
fb->changed_rev,
fb->changed_date,
fb->changed_author,
new_checksum,
(dav_prop_changes->nelts > 0)
? svn_prop_array_to_hash(
dav_prop_changes,
scratch_pool)
: NULL,
NULL ,
(! fb->shadowed) && new_base_props,
new_actual_props,
keep_recorded_info,
(fb->shadowed && fb->obstruction_found),
all_work_items,
scratch_pool));
}
if (fb->add_existed && fb->adding_file)
{
SVN_ERR(svn_wc__db_temp_op_remove_working(eb->db, fb->local_abspath,
scratch_pool));
}
apr_hash_set(fb->dir_baton->not_present_files, fb->name,
APR_HASH_KEY_STRING, NULL);
if (eb->notify_func && !fb->already_notified
&& (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
{
svn_wc_notify_t *notify;
svn_wc_notify_action_t action = svn_wc_notify_update_update;
if (fb->edited)
{
if (fb->shadowed)
action = fb->adding_file
? svn_wc_notify_update_shadowed_add
: svn_wc_notify_update_shadowed_update;
else if (fb->obstruction_found || fb->add_existed)
{
if (content_state != svn_wc_notify_state_conflicted)
action = svn_wc_notify_exists;
}
else if (fb->adding_file)
{
action = svn_wc_notify_update_add;
}
}
notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
notify->kind = svn_node_file;
notify->content_state = content_state;
notify->prop_state = prop_state;
notify->lock_state = lock_state;
notify->revision = *eb->target_revision;
notify->old_revision = fb->old_revision;
notify->mime_type = svn_prop_get_value(new_actual_props,
SVN_PROP_MIME_TYPE);
eb->notify_func(eb->notify_baton, notify, scratch_pool);
}
SVN_ERR(maybe_release_dir_info(fb->bump_info));
svn_pool_destroy(fb->pool);
return SVN_NO_ERROR;
}
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
apr_pool_t *scratch_pool = eb->pool;
if (! eb->root_opened
&& *eb->target_basename == '\0')
{
SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
eb->anchor_abspath,
scratch_pool));
}
if (! eb->target_deleted)
{
SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
eb->target_abspath,
eb->requested_depth,
eb->switch_relpath,
eb->repos_root,
eb->repos_uuid,
*(eb->target_revision),
eb->skipped_trees,
eb->pool));
if (*eb->target_basename != '\0')
{
svn_wc__db_status_t status;
svn_error_t *err;
err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
eb->db, eb->target_abspath,
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);
}
else if (status == svn_wc__db_status_excluded)
{
SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
scratch_pool));
}
}
}
apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
eb->cancel_func, eb->cancel_baton,
eb->pool));
svn_pool_destroy(eb->pool);
return SVN_NO_ERROR;
}
static svn_error_t *
make_editor(svn_revnum_t *target_revision,
svn_wc__db_t *db,
const char *anchor_abspath,
const char *target_basename,
svn_boolean_t use_commit_times,
const char *switch_url,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t adds_as_modification,
svn_boolean_t server_performs_filtering,
svn_boolean_t clean_checkout,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_dirents_func_t fetch_dirents_func,
void *fetch_dirents_baton,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_wc_external_update_t external_func,
void *external_baton,
const char *diff3_cmd,
const apr_array_header_t *preserved_exts,
const svn_delta_editor_t **editor,
void **edit_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct edit_baton *eb;
void *inner_baton;
apr_pool_t *edit_pool = svn_pool_create(result_pool);
svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
const svn_delta_editor_t *inner_editor;
const char *repos_root, *repos_uuid;
if (depth == svn_depth_unknown)
depth_is_sticky = FALSE;
SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid,
db, anchor_abspath,
result_pool, scratch_pool));
SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
_("'%s'\nis not the same repository as\n'%s'"),
switch_url, repos_root);
eb = apr_pcalloc(edit_pool, sizeof(*eb));
eb->pool = edit_pool;
eb->use_commit_times = use_commit_times;
eb->target_revision = target_revision;
eb->repos_root = repos_root;
eb->repos_uuid = repos_uuid;
eb->db = db;
eb->target_basename = target_basename;
eb->anchor_abspath = anchor_abspath;
SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
edit_pool, scratch_pool));
if (switch_url)
eb->switch_relpath =
svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
else
eb->switch_relpath = NULL;
if (svn_path_is_empty(target_basename))
eb->target_abspath = eb->anchor_abspath;
else
eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
edit_pool);
eb->requested_depth = depth;
eb->depth_is_sticky = depth_is_sticky;
eb->notify_func = notify_func;
eb->notify_baton = notify_baton;
eb->external_func = external_func;
eb->external_baton = external_baton;
eb->diff3_cmd = diff3_cmd;
eb->cancel_func = cancel_func;
eb->cancel_baton = cancel_baton;
eb->conflict_func = conflict_func;
eb->conflict_baton = conflict_baton;
eb->allow_unver_obstructions = allow_unver_obstructions;
eb->adds_as_modification = adds_as_modification;
eb->clean_checkout = clean_checkout;
eb->skipped_trees = apr_hash_make(edit_pool);
eb->dir_dirents = apr_hash_make(edit_pool);
eb->ext_patterns = preserved_exts;
apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
cleanup_edit_baton_child);
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->absent_directory = absent_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->absent_file = absent_file;
tree_editor->close_edit = close_edit;
inner_editor = tree_editor;
inner_baton = eb;
if (!depth_is_sticky
&& depth != svn_depth_unknown
&& svn_depth_empty <= depth && depth < svn_depth_infinity
&& fetch_dirents_func)
{
svn_error_t *err;
svn_wc__db_kind_t dir_kind;
svn_wc__db_status_t dir_status;
const char *dir_repos_relpath;
svn_depth_t dir_depth;
err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
&dir_repos_relpath, NULL, NULL, NULL,
NULL, NULL, &dir_depth, NULL, NULL, NULL,
NULL, NULL,
db, eb->target_abspath,
scratch_pool, scratch_pool);
if (!err
&& dir_kind == svn_wc__db_kind_dir
&& dir_status == svn_wc__db_status_normal)
{
if (dir_depth > depth)
{
apr_hash_t *dirents;
if (eb->switch_relpath)
dir_repos_relpath = eb->switch_relpath;
SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
repos_root, dir_repos_relpath,
edit_pool, scratch_pool));
if (dirents != NULL && apr_hash_count(dirents))
apr_hash_set(eb->dir_dirents,
apr_pstrdup(edit_pool, dir_repos_relpath),
APR_HASH_KEY_STRING,
dirents);
}
if (depth == svn_depth_immediates)
{
const apr_array_header_t *children;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
SVN_ERR(svn_wc__db_base_get_children(&children, db,
eb->target_abspath,
scratch_pool,
iterpool));
for (i = 0; i < children->nelts; i++)
{
const char *child_abspath;
const char *child_name;
svn_pool_clear(iterpool);
child_name = APR_ARRAY_IDX(children, i, const char *);
child_abspath = svn_dirent_join(eb->target_abspath,
child_name, iterpool);
SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
NULL, &dir_repos_relpath,
NULL, NULL, NULL, NULL,
NULL, &dir_depth, NULL,
NULL, NULL, NULL,
NULL,
db, child_abspath,
iterpool, iterpool));
if (dir_kind == svn_wc__db_kind_dir
&& dir_status == svn_wc__db_status_normal
&& dir_depth > svn_depth_empty)
{
apr_hash_t *dirents;
if (eb->switch_relpath)
dir_repos_relpath = svn_relpath_join(
eb->switch_relpath,
child_name, iterpool);
SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
repos_root, dir_repos_relpath,
edit_pool, iterpool));
if (dirents != NULL && apr_hash_count(dirents))
apr_hash_set(eb->dir_dirents,
apr_pstrdup(edit_pool, dir_repos_relpath),
APR_HASH_KEY_STRING,
dirents);
}
}
}
}
else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
svn_error_clear(err);
else
SVN_ERR(err);
}
if (!server_performs_filtering
&& !depth_is_sticky)
SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
&inner_baton,
db,
anchor_abspath,
target_basename,
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);
}
svn_error_t *
svn_wc_get_update_editor4(const svn_delta_editor_t **editor,
void **edit_baton,
svn_revnum_t *target_revision,
svn_wc_context_t *wc_ctx,
const char *anchor_abspath,
const char *target_basename,
svn_boolean_t use_commit_times,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t adds_as_modification,
svn_boolean_t server_performs_filtering,
svn_boolean_t clean_checkout,
const char *diff3_cmd,
const apr_array_header_t *preserved_exts,
svn_wc_dirents_func_t fetch_dirents_func,
void *fetch_dirents_baton,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_wc_external_update_t external_func,
void *external_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)
{
return make_editor(target_revision, wc_ctx->db, anchor_abspath,
target_basename, use_commit_times,
NULL, depth, depth_is_sticky, allow_unver_obstructions,
adds_as_modification, server_performs_filtering,
clean_checkout,
notify_func, notify_baton,
cancel_func, cancel_baton,
fetch_dirents_func, fetch_dirents_baton,
conflict_func, conflict_baton,
external_func, external_baton,
diff3_cmd, preserved_exts, editor, edit_baton,
result_pool, scratch_pool);
}
svn_error_t *
svn_wc_get_switch_editor4(const svn_delta_editor_t **editor,
void **edit_baton,
svn_revnum_t *target_revision,
svn_wc_context_t *wc_ctx,
const char *anchor_abspath,
const char *target_basename,
const char *switch_url,
svn_boolean_t use_commit_times,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t server_performs_filtering,
const char *diff3_cmd,
const apr_array_header_t *preserved_exts,
svn_wc_dirents_func_t fetch_dirents_func,
void *fetch_dirents_baton,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_wc_external_update_t external_func,
void *external_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)
{
SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
return make_editor(target_revision, wc_ctx->db, anchor_abspath,
target_basename, use_commit_times,
switch_url,
depth, depth_is_sticky, allow_unver_obstructions,
FALSE ,
server_performs_filtering,
FALSE ,
notify_func, notify_baton,
cancel_func, cancel_baton,
fetch_dirents_func, fetch_dirents_baton,
conflict_func, conflict_baton,
external_func, external_baton,
diff3_cmd, preserved_exts,
editor, edit_baton,
result_pool, scratch_pool);
}
svn_error_t *
svn_wc__check_wc_root(svn_boolean_t *wc_root,
svn_wc__db_kind_t *kind,
svn_boolean_t *switched,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
const char *parent_abspath, *name;
const char *repos_relpath, *repos_root, *repos_uuid;
svn_wc__db_status_t status;
svn_wc__db_kind_t my_kind;
if (!kind)
kind = &my_kind;
*wc_root = TRUE;
if (switched)
*switched = FALSE;
SVN_ERR(svn_wc__db_read_info(&status, kind, NULL, &repos_relpath,
&repos_root, &repos_uuid, 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 (repos_relpath == NULL)
{
*wc_root = FALSE;
return SVN_NO_ERROR;
}
if (*kind != svn_wc__db_kind_dir)
{
*wc_root = FALSE;
}
else if (status == svn_wc__db_status_added
|| status == svn_wc__db_status_deleted)
{
*wc_root = FALSE;
}
else if (status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded
|| status == svn_wc__db_status_not_present)
{
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));
}
else if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
return SVN_NO_ERROR;
if (!*wc_root && switched == NULL )
return SVN_NO_ERROR;
svn_dirent_split(&parent_abspath, &name, local_abspath, scratch_pool);
if (*wc_root)
{
svn_boolean_t is_root;
SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool));
if (is_root)
{
return SVN_NO_ERROR;
}
}
{
const char *parent_repos_root;
const char *parent_repos_relpath;
const char *parent_repos_uuid;
SVN_ERR(svn_wc__db_scan_base_repos(&parent_repos_relpath,
&parent_repos_root,
&parent_repos_uuid,
db, parent_abspath,
scratch_pool, scratch_pool));
if (strcmp(repos_root, parent_repos_root) != 0
|| strcmp(repos_uuid, parent_repos_uuid) != 0)
{
return SVN_NO_ERROR;
}
*wc_root = FALSE;
if (switched)
{
const char *expected_relpath = svn_relpath_join(parent_repos_relpath,
name, scratch_pool);
*switched = (strcmp(expected_relpath, repos_relpath) != 0);
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_is_wc_root2(svn_boolean_t *wc_root,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_boolean_t is_root;
svn_boolean_t is_switched;
svn_wc__db_kind_t kind;
svn_error_t *err;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
err = svn_wc__check_wc_root(&is_root, &kind, &is_switched,
wc_ctx->db, local_abspath, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND &&
err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
return svn_error_trace(err);
return svn_error_create(SVN_ERR_ENTRY_NOT_FOUND, err, err->message);
}
*wc_root = is_root || (kind == svn_wc__db_kind_dir && is_switched);
return SVN_NO_ERROR;
}
svn_error_t*
svn_wc__strictly_is_wc_root(svn_boolean_t *wc_root,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
return svn_error_trace(svn_wc__db_is_wcroot(wc_root,
wc_ctx->db,
local_abspath,
scratch_pool));
}
svn_error_t *
svn_wc__get_wc_root(const char **wcroot_abspath,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
return svn_wc__db_get_wcroot(wcroot_abspath, wc_ctx->db,
local_abspath, result_pool, scratch_pool);
}
svn_error_t *
svn_wc_get_actual_target2(const char **anchor,
const char **target,
svn_wc_context_t *wc_ctx,
const char *path,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t is_wc_root, is_switched;
svn_wc__db_kind_t kind;
const char *local_abspath;
svn_error_t *err;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
err = svn_wc__check_wc_root(&is_wc_root, &kind, &is_switched,
wc_ctx->db, local_abspath,
scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND &&
err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
return svn_error_trace(err);
svn_error_clear(err);
is_wc_root = FALSE;
is_switched = FALSE;
}
if (!(is_wc_root || is_switched) || (kind != svn_wc__db_kind_dir))
{
svn_dirent_split(anchor, target, path, result_pool);
}
else
{
*anchor = apr_pstrdup(result_pool, path);
*target = "";
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_stream_t *new_base_contents,
svn_stream_t *new_contents,
apr_hash_t *new_base_props,
apr_hash_t *new_props,
const char *copyfrom_url,
svn_revnum_t copyfrom_rev,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = wc_ctx->db;
const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
svn_wc__db_status_t status;
svn_wc__db_kind_t kind;
const char *tmp_text_base_abspath;
svn_checksum_t *new_text_base_md5_checksum;
svn_checksum_t *new_text_base_sha1_checksum;
const char *source_abspath = NULL;
svn_skel_t *all_work_items = NULL;
svn_skel_t *work_item;
const char *original_root_url;
const char *original_repos_relpath;
const char *original_uuid;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
svn_error_t *err;
apr_pool_t *pool = scratch_pool;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(new_base_contents != NULL);
SVN_ERR_ASSERT(new_base_props != NULL);
SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
err = svn_wc__db_read_info(&status, 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, NULL,
db, local_abspath, scratch_pool, scratch_pool);
if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_trace(err);
else if(err)
svn_error_clear(err);
else
switch (status)
{
case svn_wc__db_status_not_present:
case svn_wc__db_status_deleted:
break;
default:
return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
_("Node '%s' exists."),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
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, dir_abspath, scratch_pool, scratch_pool));
switch (status)
{
case svn_wc__db_status_normal:
case svn_wc__db_status_added:
break;
case svn_wc__db_status_deleted:
return
svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
_("Can't add '%s' to a parent directory"
" scheduled for deletion"),
svn_dirent_local_style(local_abspath,
scratch_pool));
default:
return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
_("Can't find parent directory's node while"
" trying to add '%s'"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
if (kind != svn_wc__db_kind_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Can't schedule an addition of '%s'"
" below a not-directory node"),
svn_dirent_local_style(local_abspath,
scratch_pool));
if (copyfrom_url != NULL)
{
SVN_ERR(svn_wc__internal_get_repos_info(&original_root_url,
&original_uuid,
wc_ctx->db,
dir_abspath,
pool, pool));
if (!svn_uri__is_ancestor(original_root_url, copyfrom_url))
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Copyfrom-url '%s' has different repository"
" root than '%s'"),
copyfrom_url, original_root_url);
original_repos_relpath =
svn_uri_skip_ancestor(original_root_url, copyfrom_url, pool);
}
else
{
original_root_url = NULL;
original_repos_relpath = NULL;
original_uuid = NULL;
copyfrom_rev = SVN_INVALID_REVNUM;
}
{
apr_array_header_t *regular_props;
apr_array_header_t *entry_props;
SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
&entry_props, NULL, ®ular_props,
pool));
new_base_props = svn_prop_array_to_hash(regular_props, pool);
SVN_ERR(accumulate_last_change(&changed_rev,
&changed_date,
&changed_author,
entry_props, pool, pool));
}
{
svn_stream_t *tmp_base_contents;
SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents,
&tmp_text_base_abspath,
&new_text_base_md5_checksum,
&new_text_base_sha1_checksum,
wc_ctx->db, local_abspath,
pool, pool));
SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
cancel_func, cancel_baton, pool));
}
if (new_contents)
{
const char *temp_dir_abspath;
svn_stream_t *tmp_contents;
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
local_abspath, pool, pool));
SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
temp_dir_abspath, svn_io_file_del_none,
pool, pool));
SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
cancel_func, cancel_baton, pool));
}
if (copyfrom_url != NULL)
{
SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath,
new_text_base_sha1_checksum,
new_text_base_md5_checksum, pool));
}
else
{
new_text_base_sha1_checksum = NULL;
new_text_base_md5_checksum = NULL;
}
if (new_contents == NULL && copyfrom_url == NULL)
source_abspath = tmp_text_base_abspath;
{
svn_boolean_t record_fileinfo;
record_fileinfo = (new_contents == NULL);
SVN_ERR(svn_wc__wq_build_file_install(&work_item,
db, local_abspath,
source_abspath,
FALSE ,
record_fileinfo,
pool, pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
if (source_abspath != NULL)
{
SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
db, source_abspath,
pool, pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
}
}
SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
new_base_props,
changed_rev,
changed_date,
changed_author,
original_repos_relpath,
original_root_url,
original_uuid,
copyfrom_rev,
new_text_base_sha1_checksum,
NULL ,
NULL ,
pool));
SVN_ERR(svn_wc__db_op_set_props(db, local_abspath,
new_props, FALSE,
NULL ,
all_work_items,
pool));
return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
cancel_func, cancel_baton,
pool));
}