#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_md5.h>
#include <apr_tables.h>
#include <apr_file_io.h>
#include <apr_strings.h>
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_delta.h"
#include "svn_string.h"
#include "svn_path.h"
#include "svn_xml.h"
#include "svn_error.h"
#include "svn_io.h"
#include "svn_md5.h"
#include "svn_wc.h"
#include "svn_private_config.h"
#include "svn_time.h"
#include "wc.h"
#include "log.h"
#include "adm_files.h"
#include "adm_ops.h"
#include "entries.h"
#include "lock.h"
#include "props.h"
#include "translate.h"
struct edit_baton
{
const char *anchor;
const char *target;
svn_wc_adm_access_t *adm_access;
svn_revnum_t *target_revision;
svn_boolean_t recurse;
svn_boolean_t use_commit_times;
svn_boolean_t root_opened;
svn_boolean_t target_deleted;
const char *switch_url;
const char *repos;
const char *diff3_cmd;
svn_wc_traversal_info_t *traversal_info;
svn_wc_notify_func2_t notify_func;
void *notify_baton;
svn_cancel_func_t cancel_func;
void *cancel_baton;
apr_pool_t *pool;
};
struct dir_baton
{
const char *path;
const char *name;
const char *new_URL;
struct edit_baton *edit_baton;
struct dir_baton *parent_baton;
svn_boolean_t added;
apr_array_header_t *propchanges;
struct bump_dir_info *bump_info;
int log_number;
apr_pool_t *pool;
};
struct bump_dir_info
{
struct bump_dir_info *parent;
int ref_count;
const char *path;
};
struct handler_baton
{
apr_file_t *source;
apr_file_t *dest;
svn_txdelta_window_handler_t apply_handler;
void *apply_baton;
apr_pool_t *pool;
struct file_baton *fb;
};
static const char *
get_entry_url(svn_wc_adm_access_t *associated_access,
const char *dir,
const char *name,
apr_pool_t *pool)
{
svn_error_t *err;
const svn_wc_entry_t *entry;
svn_wc_adm_access_t *adm_access;
err = svn_wc_adm_retrieve(&adm_access, associated_access, dir, pool);
if (! err)
{
err = svn_wc_entry(&entry, svn_path_join_many(pool, dir, name, NULL),
adm_access, FALSE, pool);
}
if (err || (! entry) || (! entry->url))
{
if (err)
svn_error_clear(err);
return NULL;
}
return entry->url;
}
static apr_status_t
cleanup_dir_baton(void *dir_baton)
{
struct dir_baton *db = dir_baton;
svn_error_t *err;
apr_status_t apr_err;
svn_wc_adm_access_t *adm_access;
if (db->log_number == 0)
return APR_SUCCESS;
err = svn_wc_adm_retrieve(&adm_access, db->edit_baton->adm_access,
db->path, apr_pool_parent_get(db->pool));
if (! err)
{
err = svn_wc__run_log(adm_access, NULL, apr_pool_parent_get(db->pool));
if (! err)
return APR_SUCCESS;
}
apr_err = err->apr_err;
svn_error_clear(err);
return apr_err;
}
static apr_status_t
cleanup_dir_baton_child(void *dir_baton)
{
struct dir_baton *db = dir_baton;
apr_pool_cleanup_kill(db->pool, db, cleanup_dir_baton);
return APR_SUCCESS;
}
static struct dir_baton *
make_dir_baton(const char *path,
struct edit_baton *eb,
struct dir_baton *pb,
svn_boolean_t added,
apr_pool_t *pool)
{
struct dir_baton *d = apr_pcalloc(pool, sizeof(*d));
struct bump_dir_info *bdi;
if (pb && (! path))
abort();
d->path = apr_pstrdup(pool, eb->anchor);
if (path)
{
d->path = svn_path_join(d->path, path, pool);
d->name = svn_path_basename(path, pool);
}
else
{
d->name = NULL;
}
if (eb->switch_url)
{
if (! pb)
{
if (! *eb->target)
d->new_URL = apr_pstrdup(pool, eb->switch_url);
else
d->new_URL = svn_path_dirname(eb->switch_url, pool);
}
else
{
if (*eb->target && (! pb->parent_baton))
d->new_URL = apr_pstrdup(pool, eb->switch_url);
else
d->new_URL = svn_path_url_add_component(pb->new_URL,
d->name, pool);
}
}
else
{
d->new_URL = get_entry_url(eb->adm_access, d->path, NULL, pool);
if ((! d->new_URL) && pb)
d->new_URL = svn_path_url_add_component(pb->new_URL, d->name, pool);
}
bdi = apr_palloc(eb->pool, sizeof(*bdi));
bdi->parent = pb ? pb->bump_info : NULL;
bdi->ref_count = 1;
bdi->path = apr_pstrdup(eb->pool, d->path);
if (pb)
++bdi->parent->ref_count;
d->edit_baton = eb;
d->parent_baton = pb;
d->pool = svn_pool_create(pool);
d->propchanges = apr_array_make(pool, 1, sizeof(svn_prop_t));
d->added = added;
d->bump_info = bdi;
d->log_number = 0;
apr_pool_cleanup_register(d->pool, d, cleanup_dir_baton,
cleanup_dir_baton_child);
return d;
}
static svn_error_t *
complete_directory(struct edit_baton *eb,
const char *path,
svn_boolean_t is_root_dir,
apr_pool_t *pool)
{
svn_wc_adm_access_t *adm_access;
apr_hash_t *entries;
svn_wc_entry_t *entry;
apr_hash_index_t *hi;
apr_pool_t *subpool;
svn_wc_entry_t *current_entry;
const char *name;
if (is_root_dir && *eb->target)
return SVN_NO_ERROR;
SVN_ERR(svn_wc_adm_retrieve(&adm_access, eb->adm_access, path, pool));
SVN_ERR(svn_wc_entries_read(&entries, adm_access, TRUE, pool));
entry = apr_hash_get(entries, SVN_WC_ENTRY_THIS_DIR, APR_HASH_KEY_STRING);
if (! entry)
return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
_("No '.' entry in: '%s'"),
svn_path_local_style(path, pool));
entry->incomplete = FALSE;
subpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
const void *key;
void *val;
svn_pool_clear(subpool);
apr_hash_this(hi, &key, NULL, &val);
name = key;
current_entry = val;
if (current_entry->deleted)
{
if (current_entry->schedule != svn_wc_schedule_add)
svn_wc__entry_remove(entries, name);
else
{
svn_wc_entry_t tmpentry;
tmpentry.deleted = FALSE;
SVN_ERR(svn_wc__entry_modify(adm_access, current_entry->name,
&tmpentry,
SVN_WC__ENTRY_MODIFY_DELETED,
FALSE, subpool));
}
}
else if (current_entry->absent
&& (current_entry->revision != *(eb->target_revision)))
{
svn_wc__entry_remove(entries, name);
}
else if (current_entry->kind == svn_node_dir)
{
const char *child_path = svn_path_join(path, name, subpool);
if ((svn_wc__adm_missing(adm_access, child_path))
&& (! current_entry->absent)
&& (current_entry->schedule != svn_wc_schedule_add))
{
svn_wc__entry_remove(entries, name);
if (eb->notify_func)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(child_path,
svn_wc_notify_update_delete,
subpool);
notify->kind = current_entry->kind;
(* eb->notify_func)(eb->notify_baton, notify, subpool);
}
}
}
}
svn_pool_destroy(subpool);
SVN_ERR(svn_wc__entries_write(entries, adm_access, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
maybe_bump_dir_info(struct edit_baton *eb,
struct bump_dir_info *bdi,
apr_pool_t *pool)
{
for ( ; bdi != NULL; bdi = bdi->parent)
{
if (--bdi->ref_count > 0)
return SVN_NO_ERROR;
SVN_ERR(complete_directory(eb, bdi->path,
bdi->parent ? FALSE : TRUE, pool));
}
return SVN_NO_ERROR;
}
struct file_baton
{
struct edit_baton *edit_baton;
struct dir_baton *dir_baton;
apr_pool_t *pool;
const char *name;
const char *path;
const char *new_URL;
svn_boolean_t added;
svn_boolean_t text_changed;
svn_boolean_t prop_changed;
apr_array_header_t *propchanges;
const char *last_changed_date;
struct bump_dir_info *bump_info;
unsigned char digest[APR_MD5_DIGESTSIZE];
};
static struct file_baton *
make_file_baton(struct dir_baton *pb,
const char *path,
svn_boolean_t adding,
apr_pool_t *pool)
{
struct file_baton *f = apr_pcalloc(pool, sizeof(*f));
if (! path)
abort();
f->path = svn_path_join(pb->edit_baton->anchor, path, pool);
f->name = svn_path_basename(path, pool);
if (pb->edit_baton->switch_url)
{
f->new_URL = svn_path_url_add_component(pb->new_URL, f->name, pool);
}
else
{
f->new_URL = get_entry_url(pb->edit_baton->adm_access,
pb->path, f->name, pool);
}
f->pool = pool;
f->edit_baton = pb->edit_baton;
f->propchanges = apr_array_make(pool, 1, sizeof(svn_prop_t));
f->bump_info = pb->bump_info;
f->added = adding;
f->dir_baton = pb;
++f->bump_info->ref_count;
return f;
}
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_error_t *err = SVN_NO_ERROR, *err2 = SVN_NO_ERROR;
err = hb->apply_handler(window, hb->apply_baton);
if (window != NULL && err == SVN_NO_ERROR)
return err;
if (hb->source)
{
err2 = svn_wc__close_text_base(hb->source, fb->path, 0, fb->pool);
if (err2 != SVN_NO_ERROR && err == SVN_NO_ERROR)
err = err2;
else
svn_error_clear(err2);
}
err2 = svn_wc__close_text_base(hb->dest, fb->path, 0, fb->pool);
if (err2 != SVN_NO_ERROR)
{
if (err == SVN_NO_ERROR)
err = err2;
else
svn_error_clear(err2);
}
svn_pool_destroy(hb->pool);
if (err != SVN_NO_ERROR)
{
const char *tmppath = svn_wc__text_base_path(fb->path, TRUE, fb->pool);
apr_file_remove(tmppath, fb->pool);
}
else
{
fb->text_changed = 1;
}
return err;
}
static svn_error_t *
prep_directory(struct dir_baton *db,
const char *ancestor_url,
svn_revnum_t ancestor_revision,
apr_pool_t *pool)
{
const char *repos;
SVN_ERR(svn_wc__ensure_directory(db->path, pool));
if (db->edit_baton->repos
&& svn_path_is_ancestor(db->edit_baton->repos, ancestor_url))
repos = db->edit_baton->repos;
else
repos = NULL;
SVN_ERR(svn_wc_ensure_adm2(db->path, NULL,
ancestor_url, repos,
ancestor_revision, pool));
if (! db->edit_baton->adm_access
|| strcmp(svn_wc_adm_access_path(db->edit_baton->adm_access),
db->path))
{
svn_wc_adm_access_t *adm_access;
apr_pool_t *adm_access_pool
= db->edit_baton->adm_access
? svn_wc_adm_access_pool(db->edit_baton->adm_access)
: db->edit_baton->pool;
SVN_ERR(svn_wc_adm_open3(&adm_access, db->edit_baton->adm_access,
db->path, TRUE, 0, NULL, NULL,
adm_access_pool));
if (!db->edit_baton->adm_access)
db->edit_baton->adm_access = adm_access;
}
return SVN_NO_ERROR;
}
static svn_error_t *
accumulate_entry_props(svn_stringbuf_t *log_accum,
svn_wc_notify_lock_state_t *lock_state,
svn_wc_adm_access_t *adm_access,
const char *base_name,
apr_array_header_t *entry_props,
apr_pool_t *pool)
{
int i;
svn_wc_entry_t tmp_entry;
apr_uint32_t flags = 0;
if (lock_state)
*lock_state = svn_wc_notify_lock_state_unchanged;
for (i = 0; i < entry_props->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
const char *val;
if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
{
SVN_ERR(svn_wc__loggy_delete_lock(&log_accum, adm_access,
base_name, pool));
if (lock_state)
*lock_state = svn_wc_notify_lock_state_unlocked;
continue;
}
if (! prop->value)
continue;
val = prop->value->data;
if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
{
flags |= SVN_WC__ENTRY_MODIFY_CMT_AUTHOR;
tmp_entry.cmt_author = val;
}
else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
{
flags |= SVN_WC__ENTRY_MODIFY_CMT_REV;
tmp_entry.cmt_rev = SVN_STR_TO_REV(val);
}
else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
{
flags |= SVN_WC__ENTRY_MODIFY_CMT_DATE;
SVN_ERR(svn_time_from_cstring(&tmp_entry.cmt_date, val, pool));
}
else if (! strcmp(prop->name, SVN_PROP_ENTRY_UUID))
{
flags |= SVN_WC__ENTRY_MODIFY_UUID;
tmp_entry.uuid = val;
}
}
if (flags)
SVN_ERR(svn_wc__loggy_entry_modify(&log_accum, adm_access, base_name,
&tmp_entry, flags, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
accumulate_wcprops(svn_stringbuf_t *log_accum,
svn_wc_adm_access_t *adm_access,
const char *base_name,
apr_array_header_t *wcprops,
apr_pool_t *pool)
{
int i;
for (i = 0; i < wcprops->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(wcprops, i, svn_prop_t);
SVN_ERR(svn_wc__loggy_modify_wcprop
(&log_accum, adm_access,
base_name,
prop->name, prop->value ? prop->value->data : NULL, 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 *d;
eb->root_opened = TRUE;
*dir_baton = d = make_dir_baton(NULL, eb, NULL, FALSE, pool);
if (! *eb->target)
{
svn_wc_adm_access_t *adm_access;
svn_wc_entry_t tmp_entry;
apr_uint32_t flags = SVN_WC__ENTRY_MODIFY_REVISION |
SVN_WC__ENTRY_MODIFY_URL | SVN_WC__ENTRY_MODIFY_INCOMPLETE;
tmp_entry.revision = *(eb->target_revision);
tmp_entry.url = d->new_URL;
if (eb->repos && svn_path_is_ancestor(eb->repos, d->new_URL))
{
tmp_entry.repos = eb->repos;
flags |= SVN_WC__ENTRY_MODIFY_REPOS;
}
tmp_entry.incomplete = TRUE;
SVN_ERR(svn_wc_adm_retrieve(&adm_access, eb->adm_access,
d->path, pool));
SVN_ERR(svn_wc__entry_modify(adm_access, NULL ,
&tmp_entry, flags,
TRUE ,
pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
leftmod_error_chain(svn_error_t *err,
const char *logfile,
const char *path,
apr_pool_t *pool)
{
svn_error_t *tmp_err;
if (! err)
return SVN_NO_ERROR;
for (tmp_err = err; tmp_err; tmp_err = tmp_err->child)
if (tmp_err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
break;
if (tmp_err)
{
svn_error_clear(svn_io_remove_file(logfile, pool));
return svn_error_createf
(SVN_ERR_WC_OBSTRUCTED_UPDATE, tmp_err,
_("Won't delete locally modified directory '%s'"),
svn_path_local_style(path, pool));
}
return err;
}
static svn_error_t *
do_entry_deletion(struct edit_baton *eb,
const char *parent_path,
const char *path,
int *log_number,
apr_pool_t *pool)
{
apr_file_t *log_fp = NULL;
const char *base_name;
svn_wc_adm_access_t *adm_access;
svn_node_kind_t kind;
const char *logfile_path, *logfile_name;
const char *full_path = svn_path_join(eb->anchor, path, pool);
svn_stringbuf_t *log_item = svn_stringbuf_create("", pool);
SVN_ERR(svn_io_check_path(full_path, &kind, pool));
SVN_ERR(svn_wc_adm_retrieve(&adm_access, eb->adm_access,
parent_path, pool));
logfile_name = svn_wc__logfile_path(*log_number, pool);
logfile_path = svn_wc__adm_path(parent_path, FALSE, pool,
logfile_name, NULL);
SVN_ERR(svn_wc__open_adm_file(&log_fp,
parent_path,
logfile_name,
(APR_WRITE | APR_CREATE),
pool));
base_name = svn_path_basename(path, pool);
SVN_ERR(svn_wc__loggy_delete_entry(&log_item, adm_access,
base_name, pool));
if (strcmp(path, eb->target) == 0)
{
svn_wc_entry_t tmp_entry;
tmp_entry.revision = *(eb->target_revision);
tmp_entry.kind = (kind == svn_node_file) ? svn_node_file : svn_node_dir;
tmp_entry.deleted = TRUE;
SVN_ERR(svn_wc__loggy_entry_modify(&log_item, adm_access,
path, &tmp_entry,
SVN_WC__ENTRY_MODIFY_REVISION
| SVN_WC__ENTRY_MODIFY_KIND
| SVN_WC__ENTRY_MODIFY_DELETED,
pool));
eb->target_deleted = TRUE;
}
SVN_ERR_W(svn_io_file_write_full(log_fp, log_item->data,
log_item->len, NULL, pool),
apr_psprintf(pool,
_("Error writing log file for '%s'"),
svn_path_local_style(parent_path, pool)));
SVN_ERR(svn_wc__close_adm_file(log_fp,
parent_path,
logfile_name,
TRUE,
pool));
if (eb->switch_url)
{
if (kind == svn_node_dir)
{
svn_wc_adm_access_t *child_access;
SVN_ERR(svn_wc_adm_retrieve
(&child_access, eb->adm_access,
full_path, pool));
SVN_ERR(leftmod_error_chain
(svn_wc_remove_from_revision_control
(child_access,
SVN_WC_ENTRY_THIS_DIR,
TRUE,
TRUE,
eb->cancel_func,
eb->cancel_baton,
pool),
logfile_path, parent_path, pool));
}
}
SVN_ERR(leftmod_error_chain(svn_wc__run_log(adm_access, NULL, pool),
logfile_path, parent_path, pool));
*log_number = 0;
if (eb->notify_func)
(*eb->notify_func)
(eb->notify_baton,
svn_wc_create_notify(svn_path_join(parent_path, base_name, pool),
svn_wc_notify_update_delete, pool), pool);
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;
return do_entry_deletion(pb->edit_baton, pb->path, path, &pb->log_number,
pool);
}
static svn_error_t *
add_directory(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
struct dir_baton *db = make_dir_baton(path, eb, pb, TRUE, pool);
svn_node_kind_t kind;
if ((copyfrom_path && (! SVN_IS_VALID_REVNUM(copyfrom_revision)))
|| ((! copyfrom_path) && (SVN_IS_VALID_REVNUM(copyfrom_revision))))
abort();
SVN_ERR(svn_io_check_path(db->path, &kind, db->pool));
if (kind != svn_node_none)
return svn_error_createf
(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to add directory '%s': "
"object of the same name already exists"),
svn_path_local_style(db->path, pool));
if (svn_wc_is_adm_dir(svn_path_basename(path, pool), 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_path_local_style(db->path, pool));
if (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_revision))
{
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Failed to add directory '%s': "
"copyfrom arguments not yet supported"),
svn_path_local_style(db->path, pool));
}
else
{
svn_wc_adm_access_t *adm_access;
const svn_wc_entry_t *dir_entry;
apr_hash_t *entries;
svn_wc_entry_t tmp_entry;
SVN_ERR(svn_wc_adm_retrieve(&adm_access, eb->adm_access,
pb->path, db->pool));
SVN_ERR(svn_wc_entries_read(&entries, adm_access, FALSE, db->pool));
dir_entry = apr_hash_get(entries, db->name, APR_HASH_KEY_STRING);
if (dir_entry && dir_entry->schedule == svn_wc_schedule_add)
return svn_error_createf
(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to add directory '%s': object of the same name "
"is already scheduled for addition"),
svn_path_local_style(path, pool));
tmp_entry.kind = svn_node_dir;
tmp_entry.deleted = FALSE;
tmp_entry.absent = FALSE;
SVN_ERR(svn_wc__entry_modify(adm_access, db->name, &tmp_entry,
(SVN_WC__ENTRY_MODIFY_KIND |
SVN_WC__ENTRY_MODIFY_DELETED |
SVN_WC__ENTRY_MODIFY_ABSENT),
TRUE , pool));
}
SVN_ERR(prep_directory(db,
db->new_URL,
*(eb->target_revision),
db->pool));
*child_baton = db;
if (eb->notify_func)
{
svn_wc_notify_t *notify = svn_wc_create_notify(db->path,
svn_wc_notify_update_add,
pool);
notify->kind = svn_node_dir;
(*eb->notify_func)(eb->notify_baton, notify, pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
svn_wc_entry_t tmp_entry;
apr_uint32_t flags = SVN_WC__ENTRY_MODIFY_REVISION |
SVN_WC__ENTRY_MODIFY_URL | SVN_WC__ENTRY_MODIFY_INCOMPLETE;
svn_wc_adm_access_t *adm_access;
struct dir_baton *db = make_dir_baton(path, eb, pb, FALSE, pool);
*child_baton = db;
tmp_entry.revision = *(eb->target_revision);
tmp_entry.url = db->new_URL;
if (eb->repos && svn_path_is_ancestor(eb->repos, db->new_URL))
{
tmp_entry.repos = eb->repos;
flags |= SVN_WC__ENTRY_MODIFY_REPOS;
}
tmp_entry.incomplete = TRUE;
SVN_ERR(svn_wc_adm_retrieve(&adm_access, eb->adm_access,
db->path, pool));
SVN_ERR(svn_wc__entry_modify(adm_access, NULL ,
&tmp_entry, flags,
TRUE ,
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;
propchange = apr_array_push(db->propchanges);
propchange->name = apr_pstrdup(db->pool, name);
propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
return SVN_NO_ERROR;
}
static const svn_prop_t *
externals_prop_changed(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;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
apr_array_header_t *entry_props, *wc_props, *regular_props;
svn_wc_adm_access_t *adm_access;
SVN_ERR(svn_categorize_props(db->propchanges, &entry_props, &wc_props,
®ular_props, pool));
SVN_ERR(svn_wc_adm_retrieve(&adm_access, db->edit_baton->adm_access,
db->path, db->pool));
if (regular_props->nelts || entry_props->nelts || wc_props->nelts)
{
svn_stringbuf_t *entry_accum = svn_stringbuf_create("", db->pool);
if (regular_props->nelts)
{
if (db->edit_baton->traversal_info)
{
svn_wc_traversal_info_t *ti = db->edit_baton->traversal_info;
const svn_prop_t *change = externals_prop_changed(regular_props);
if (change)
{
const svn_string_t *new_val_s = change->value;
const svn_string_t *old_val_s;
SVN_ERR(svn_wc_prop_get
(&old_val_s, SVN_PROP_EXTERNALS,
db->path, adm_access, db->pool));
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)
apr_hash_set(ti->externals_old,
apr_pstrdup(ti->pool, db->path),
APR_HASH_KEY_STRING,
apr_pstrmemdup(ti->pool, old_val_s->data,
old_val_s->len));
if (new_val_s)
apr_hash_set(ti->externals_new,
apr_pstrdup(ti->pool, db->path),
APR_HASH_KEY_STRING,
apr_pstrmemdup(ti->pool, new_val_s->data,
new_val_s->len));
}
}
}
SVN_ERR_W(svn_wc__merge_props(&prop_state,
adm_access, NULL,
NULL ,
regular_props, TRUE, FALSE,
db->pool, &entry_accum),
_("Couldn't do property merge"));
}
SVN_ERR(accumulate_entry_props(entry_accum, NULL,
adm_access, SVN_WC_ENTRY_THIS_DIR,
entry_props, pool));
SVN_ERR(accumulate_wcprops(entry_accum, adm_access,
SVN_WC_ENTRY_THIS_DIR, wc_props, pool));
SVN_ERR(svn_wc__write_log(adm_access, db->log_number, entry_accum, pool));
}
SVN_ERR(svn_wc__run_log(adm_access, db->edit_baton->diff3_cmd, db->pool));
db->log_number = 0;
SVN_ERR(maybe_bump_dir_info(db->edit_baton, db->bump_info, db->pool));
if ((! db->added) && (db->edit_baton->notify_func))
{
svn_wc_notify_t *notify
= svn_wc_create_notify(db->path, svn_wc_notify_update_update, pool);
notify->kind = svn_node_dir;
notify->prop_state = prop_state;
(*db->edit_baton->notify_func)(db->edit_baton->notify_baton,
notify, pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
absent_file_or_dir(const char *path,
svn_node_kind_t kind,
void *parent_baton,
apr_pool_t *pool)
{
const char *name = svn_path_basename(path, pool);
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
svn_wc_adm_access_t *adm_access;
apr_hash_t *entries;
const svn_wc_entry_t *ent;
svn_wc_entry_t tmp_entry;
SVN_ERR(svn_wc_adm_retrieve(&adm_access, eb->adm_access, pb->path, pool));
SVN_ERR(svn_wc_entries_read(&entries, adm_access, FALSE, pool));
ent = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
if (ent && (ent->schedule == svn_wc_schedule_add))
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_path_local_style(path, pool));
tmp_entry.kind = kind;
tmp_entry.deleted = FALSE;
tmp_entry.revision = *(eb->target_revision);
tmp_entry.absent = TRUE;
SVN_ERR(svn_wc__entry_modify(adm_access, name, &tmp_entry,
(SVN_WC__ENTRY_MODIFY_KIND |
SVN_WC__ENTRY_MODIFY_REVISION |
SVN_WC__ENTRY_MODIFY_DELETED |
SVN_WC__ENTRY_MODIFY_ABSENT),
TRUE , pool));
return SVN_NO_ERROR;
}
static svn_error_t *
absent_file(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
return absent_file_or_dir(path, svn_node_file, parent_baton, pool);
}
static svn_error_t *
absent_directory(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
return absent_file_or_dir(path, svn_node_dir, parent_baton, pool);
}
static svn_error_t *
add_or_open_file(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
void **file_baton,
svn_boolean_t adding,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
struct file_baton *fb;
const svn_wc_entry_t *entry;
svn_node_kind_t kind;
svn_wc_adm_access_t *adm_access;
apr_pool_t *subpool = svn_pool_create(pool);
fb = make_file_baton(pb, path, adding, pool);
SVN_ERR(svn_io_check_path(fb->path, &kind, subpool));
SVN_ERR(svn_wc_adm_retrieve(&adm_access, eb->adm_access,
pb->path, subpool));
SVN_ERR(svn_wc_entry(&entry, fb->path, adm_access, FALSE, subpool));
if (adding && (kind != svn_node_none))
return svn_error_createf
(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to add file '%s': object of the same name already exists"),
svn_path_local_style(fb->path, pool));
if (adding && entry && entry->schedule == svn_wc_schedule_add)
return svn_error_createf
(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("Failed to add file '%s': object of the same name is already "
"scheduled for addition"),
svn_path_local_style(fb->path, pool));
if ((! adding) && (! entry))
return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
_("File '%s' in directory '%s' "
"is not a versioned resource"),
fb->name,
svn_path_local_style(pb->path, pool));
svn_pool_destroy(subpool);
*file_baton = fb;
return SVN_NO_ERROR;
}
static svn_error_t *
add_file(const char *name,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
apr_pool_t *pool,
void **file_baton)
{
return add_or_open_file(name, parent_baton, copyfrom_path,
copyfrom_revision, file_baton, TRUE, pool);
}
static svn_error_t *
open_file(const char *name,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **file_baton)
{
return add_or_open_file(name, parent_baton, NULL, base_revision,
file_baton, FALSE, pool);
}
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_checksum,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
struct file_baton *fb = file_baton;
struct edit_baton *eb = fb->edit_baton;
apr_pool_t *handler_pool = svn_pool_create(fb->pool);
struct handler_baton *hb = apr_palloc(handler_pool, sizeof(*hb));
svn_error_t *err;
svn_wc_adm_access_t *adm_access;
const svn_wc_entry_t *ent;
svn_boolean_t replaced;
svn_boolean_t use_revert_base;
hb->source = NULL;
SVN_ERR(svn_wc_adm_retrieve(&adm_access, eb->adm_access,
svn_path_dirname(fb->path, pool), pool));
SVN_ERR(svn_wc_entry(&ent, fb->path, adm_access, FALSE, pool));
replaced = ent && ent->schedule == svn_wc_schedule_replace;
use_revert_base = replaced && (ent->copyfrom_url != NULL);
if (ent && ent->checksum)
{
unsigned char digest[APR_MD5_DIGESTSIZE];
const char *hex_digest;
const char *tb;
if (use_revert_base)
tb = svn_wc__text_revert_path(fb->path, FALSE, pool);
else
tb = svn_wc__text_base_path(fb->path, FALSE, pool);
SVN_ERR(svn_io_file_checksum(digest, tb, pool));
hex_digest = svn_md5_digest_to_cstring_display(digest, pool);
if (base_checksum)
{
if (strcmp(hex_digest, base_checksum) != 0)
return svn_error_createf
(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
_("Checksum mismatch for '%s'; expected: '%s', actual: '%s'"),
svn_path_local_style(tb, pool), base_checksum, hex_digest);
}
if ((ent && ent->checksum) && ! replaced &&
strcmp(hex_digest, ent->checksum) != 0)
{
return svn_error_createf
(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
_("Checksum mismatch for '%s'; recorded: '%s', actual: '%s'"),
svn_path_local_style(tb, pool), ent->checksum, hex_digest);
}
}
if (use_revert_base)
err = svn_wc__open_revert_base(&hb->source, fb->path,
APR_READ,
handler_pool);
else
err = svn_wc__open_text_base(&hb->source, fb->path, APR_READ,
handler_pool);
if (err && !APR_STATUS_IS_ENOENT(err->apr_err))
{
if (hb->source)
{
if (use_revert_base)
svn_error_clear(svn_wc__close_revert_base(hb->source, fb->path,
0, handler_pool));
else
svn_error_clear(svn_wc__close_text_base(hb->source, fb->path,
0, handler_pool));
}
svn_pool_destroy(handler_pool);
return err;
}
else if (err)
{
svn_error_clear(err);
hb->source = NULL;
}
hb->dest = NULL;
if (use_revert_base)
err = svn_wc__open_revert_base(&hb->dest, fb->path,
(APR_WRITE | APR_TRUNCATE | APR_CREATE),
handler_pool);
else
err = svn_wc__open_text_base(&hb->dest, fb->path,
(APR_WRITE | APR_TRUNCATE | APR_CREATE),
handler_pool);
if (err)
{
if (hb->dest)
{
if (replaced)
svn_error_clear(svn_wc__close_revert_base(hb->dest, fb->path, 0,
handler_pool));
else
svn_error_clear(svn_wc__close_text_base(hb->dest, fb->path, 0,
handler_pool));
}
svn_pool_destroy(handler_pool);
return err;
}
{
const char *tmp_path;
apr_file_name_get(&tmp_path, hb->dest);
svn_txdelta_apply(svn_stream_from_aprfile(hb->source, handler_pool),
svn_stream_from_aprfile(hb->dest, handler_pool),
fb->digest, tmp_path, 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 *pool)
{
struct file_baton *fb = file_baton;
struct edit_baton *eb = fb->edit_baton;
svn_prop_t *propchange;
propchange = apr_array_push(fb->propchanges);
propchange->name = apr_pstrdup(fb->pool, name);
propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
fb->prop_changed = 1;
if (eb->use_commit_times
&& (strcmp(name, SVN_PROP_ENTRY_COMMITTED_DATE) == 0)
&& value)
fb->last_changed_date = apr_pstrdup(fb->pool, value->data);
return SVN_NO_ERROR;
}
static svn_error_t *
merge_props(svn_stringbuf_t *log_accum,
svn_wc_notify_state_t *prop_state,
svn_wc_notify_lock_state_t *lock_state,
svn_wc_adm_access_t *adm_access,
const char *file_path,
const apr_array_header_t *prop_changes,
apr_pool_t *pool)
{
apr_array_header_t *regular_props = NULL, *wc_props = NULL,
*entry_props = NULL;
const char *base_name;
svn_path_split(file_path, NULL, &base_name, pool);
SVN_ERR(svn_categorize_props(prop_changes,
&entry_props, &wc_props, ®ular_props,
pool));
if (prop_state)
*prop_state = svn_wc_notify_state_unknown;
if (regular_props)
{
SVN_ERR(svn_wc__merge_props(prop_state,
adm_access, base_name,
NULL ,
regular_props, TRUE, FALSE, pool,
&log_accum));
}
if (entry_props)
SVN_ERR(accumulate_entry_props(log_accum, lock_state,
adm_access, base_name,
entry_props, pool));
else
*lock_state = svn_wc_notify_lock_state_unchanged;
if (regular_props)
{
svn_boolean_t prop_modified;
SVN_ERR(svn_wc_props_modified_p(&prop_modified,
file_path, adm_access,
pool));
if (! prop_modified)
SVN_ERR(svn_wc__loggy_set_entry_timestamp_from_wc
(&log_accum, adm_access,
base_name, SVN_WC__ENTRY_ATTR_PROP_TIME, pool));
}
if (wc_props)
SVN_ERR(accumulate_wcprops(log_accum, adm_access,
base_name, wc_props, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
tweak_entry(svn_stringbuf_t *log_accum,
svn_wc_adm_access_t *adm_access,
const char *name,
svn_revnum_t new_revision,
const char *new_URL,
apr_pool_t *pool)
{
svn_wc_entry_t tmp_entry;
apr_uint32_t modify_flags = SVN_WC__ENTRY_MODIFY_KIND
| SVN_WC__ENTRY_MODIFY_REVISION
| SVN_WC__ENTRY_MODIFY_DELETED
| SVN_WC__ENTRY_MODIFY_ABSENT;
tmp_entry.revision = new_revision;
tmp_entry.kind = svn_node_file;
tmp_entry.deleted = FALSE;
tmp_entry.absent = FALSE;
if (new_URL)
{
tmp_entry.url = new_URL;
modify_flags |= SVN_WC__ENTRY_MODIFY_URL;
}
SVN_ERR(svn_wc__loggy_entry_modify(&log_accum, adm_access,
name, &tmp_entry, modify_flags,
pool));
return SVN_NO_ERROR;
}
static svn_error_t *
merge_file(svn_stringbuf_t *log_accum,
svn_wc_notify_state_t *content_state,
svn_wc_notify_state_t *prop_state,
svn_wc_notify_lock_state_t *lock_state,
svn_wc_adm_access_t *adm_access,
const char *file_path,
svn_revnum_t new_revision,
svn_boolean_t has_new_text_base,
const apr_array_header_t *prop_changes,
const char *new_URL,
const char *diff3_cmd,
const char *timestamp_string,
apr_pool_t *pool)
{
const char *parent_dir, *base_name;
const char *new_text_path = NULL;
svn_boolean_t is_locally_modified;
svn_boolean_t is_replaced = FALSE;
svn_boolean_t magic_props_changed = FALSE;
svn_boolean_t use_revert_base = FALSE;
enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
svn_wc_notify_lock_state_t local_lock_state;
const char *txtb = NULL, *tmp_txtb = NULL;
if (! lock_state)
lock_state = &local_lock_state;
svn_path_split(file_path, &parent_dir, &base_name, pool);
if (has_new_text_base)
new_text_path = svn_wc__text_base_path(file_path, TRUE, pool);
magic_props_changed = svn_wc__has_magic_property(prop_changes);
SVN_ERR(merge_props(log_accum, prop_state, lock_state, adm_access,
file_path, prop_changes, pool));
SVN_ERR(svn_wc_text_modified_p(&is_locally_modified, file_path,
FALSE, adm_access, pool));
if (!is_locally_modified)
{
const svn_wc_entry_t *entry;
SVN_ERR(svn_wc_entry(&entry, file_path, adm_access, FALSE, pool));
if (entry && entry->schedule == svn_wc_schedule_replace)
is_replaced = TRUE;
if (is_replaced && entry->copyfrom_url)
use_revert_base = TRUE;
}
if (new_text_path)
{
if (!use_revert_base)
{
txtb = svn_wc__text_base_path(base_name, FALSE, pool);
tmp_txtb = svn_wc__text_base_path(base_name, TRUE, pool);
}
else
{
txtb = svn_wc__text_revert_path(base_name, FALSE, pool);
tmp_txtb = svn_wc__text_revert_path(base_name, TRUE, pool);
}
}
else
{
apr_hash_t *keywords;
SVN_ERR(svn_wc__get_keywords(&keywords, file_path,
adm_access, NULL, pool));
if (magic_props_changed || keywords)
{
const char *tmptext;
SVN_ERR(svn_wc_translated_file2(&tmptext, file_path, file_path,
adm_access,
SVN_WC_TRANSLATE_TO_NF
| SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
pool));
tmptext = svn_path_is_child(parent_dir, tmptext, pool);
SVN_ERR(svn_wc__loggy_copy(&log_accum, NULL, adm_access,
svn_wc__copy_translate,
tmptext, base_name, FALSE, pool));
}
}
SVN_ERR(tweak_entry(log_accum, adm_access, base_name,
new_revision, new_URL, pool));
if (new_text_path)
{
if (! is_locally_modified && ! is_replaced)
{
SVN_ERR(svn_wc__loggy_copy(&log_accum, NULL, adm_access,
svn_wc__copy_translate,
tmp_txtb, base_name, FALSE, pool));
}
else
{
svn_node_kind_t wfile_kind = svn_node_unknown;
SVN_ERR(svn_io_check_path(file_path, &wfile_kind, pool));
if (wfile_kind == svn_node_none)
{
SVN_ERR(svn_wc__loggy_copy(&log_accum, NULL, adm_access,
svn_wc__copy_translate,
tmp_txtb, base_name, FALSE, pool));
}
else
{
const char *oldrev_str, *newrev_str;
const svn_wc_entry_t *e;
const char *base;
SVN_ERR(svn_wc_entry(&e, file_path, adm_access, FALSE, pool));
if (! e)
return svn_error_createf(
SVN_ERR_UNVERSIONED_RESOURCE, NULL,
_("'%s' is not under version control"),
svn_path_local_style(file_path, pool));
oldrev_str = apr_psprintf(pool, ".r%ld",
e->revision);
newrev_str = apr_psprintf(pool, ".r%ld",
new_revision);
base = svn_wc_adm_access_path(adm_access);
SVN_ERR(svn_wc__merge_internal
(&log_accum, &merge_outcome,
svn_path_join(base, txtb, pool),
svn_path_join(base, tmp_txtb, pool),
svn_path_join(base, base_name, pool),
adm_access,
oldrev_str, newrev_str, ".mine",
FALSE, diff3_cmd, NULL,
pool));
}
}
}
else if (*lock_state == svn_wc_notify_lock_state_unlocked)
SVN_ERR(svn_wc__loggy_maybe_set_readonly(&log_accum, adm_access,
base_name, pool));
if (new_text_path)
{
SVN_ERR(svn_wc__loggy_move(&log_accum, NULL,
adm_access, tmp_txtb, txtb, FALSE, pool));
SVN_ERR(svn_wc__loggy_set_readonly(&log_accum, adm_access,
txtb, pool));
if (!is_replaced)
{
svn_wc_entry_t tmp_entry;
unsigned char digest[APR_MD5_DIGESTSIZE];
SVN_ERR(svn_io_file_checksum(digest, new_text_path, pool));
tmp_entry.checksum = svn_md5_digest_to_cstring(digest, pool);
SVN_ERR(svn_wc__loggy_entry_modify(&log_accum, adm_access,
base_name, &tmp_entry,
SVN_WC__ENTRY_MODIFY_CHECKSUM,
pool));
}
}
if (!is_locally_modified)
{
if (timestamp_string)
SVN_ERR(svn_wc__loggy_set_timestamp(&log_accum, adm_access,
base_name, timestamp_string,
pool));
if (new_text_path || magic_props_changed)
SVN_ERR(svn_wc__loggy_set_entry_timestamp_from_wc
(&log_accum, adm_access,
base_name, SVN_WC__ENTRY_ATTR_TEXT_TIME, pool));
}
if (content_state)
{
*content_state = svn_wc_notify_state_unknown;
if (merge_outcome == svn_wc_merge_conflict)
*content_state = svn_wc_notify_state_conflicted;
else if (new_text_path)
{
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 *text_checksum,
apr_pool_t *pool)
{
struct file_baton *fb = file_baton;
struct edit_baton *eb = fb->edit_baton;
const char *parent_path;
apr_array_header_t *propchanges = NULL;
svn_wc_notify_state_t content_state, prop_state;
svn_wc_notify_lock_state_t lock_state;
svn_wc_adm_access_t *adm_access;
svn_stringbuf_t *log_accum;
if (fb->text_changed && text_checksum)
{
const char *real_sum = svn_md5_digest_to_cstring(fb->digest, pool);
if (real_sum && (strcmp(text_checksum, real_sum) != 0))
return svn_error_createf
(SVN_ERR_CHECKSUM_MISMATCH, NULL,
_("Checksum mismatch for '%s'; expected: '%s', actual: '%s'"),
svn_path_local_style(fb->path, pool), text_checksum, real_sum);
}
if (fb->prop_changed)
propchanges = fb->propchanges;
parent_path = svn_path_dirname(fb->path, pool);
SVN_ERR(svn_wc_adm_retrieve(&adm_access, eb->adm_access,
parent_path, pool));
log_accum = svn_stringbuf_create("", pool);
SVN_ERR(merge_file(log_accum,
&content_state,
&prop_state,
&lock_state,
adm_access,
fb->path,
(*eb->target_revision),
fb->text_changed,
propchanges,
fb->new_URL,
eb->diff3_cmd,
fb->last_changed_date,
pool));
SVN_ERR(svn_wc__write_log(adm_access, fb->dir_baton->log_number,
log_accum, pool));
fb->dir_baton->log_number++;
SVN_ERR(maybe_bump_dir_info(eb, fb->bump_info, pool));
if (((content_state != svn_wc_notify_state_unchanged) ||
(prop_state != svn_wc_notify_state_unchanged) ||
(lock_state != svn_wc_notify_lock_state_unchanged)) &&
eb->notify_func)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(fb->path,
fb->added ? svn_wc_notify_update_add
: svn_wc_notify_update_update, pool);
notify->kind = svn_node_file;
notify->content_state = content_state;
notify->prop_state = prop_state;
notify->lock_state = lock_state;
(*eb->notify_func)(eb->notify_baton, notify, pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
const char *target_path = svn_path_join(eb->anchor, eb->target, pool);
int log_number = 0;
if ((*eb->target) && (svn_wc__adm_missing(eb->adm_access, target_path)))
SVN_ERR(do_entry_deletion(eb, eb->anchor, eb->target, &log_number,
pool));
if (! eb->root_opened)
{
SVN_ERR(complete_directory(eb, eb->anchor, TRUE, pool));
}
if (! eb->target_deleted)
SVN_ERR(svn_wc__do_update_cleanup(target_path,
eb->adm_access,
eb->recurse,
eb->switch_url,
eb->repos,
*(eb->target_revision),
eb->notify_func,
eb->notify_baton,
TRUE,
eb->pool));
svn_pool_destroy(eb->pool);
return SVN_NO_ERROR;
}
static svn_error_t *
make_editor(svn_revnum_t *target_revision,
svn_wc_adm_access_t *adm_access,
const char *anchor,
const char *target,
svn_boolean_t use_commit_times,
const char *switch_url,
svn_boolean_t recurse,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
const char *diff3_cmd,
const svn_delta_editor_t **editor,
void **edit_baton,
svn_wc_traversal_info_t *traversal_info,
apr_pool_t *pool)
{
struct edit_baton *eb;
apr_pool_t *subpool = svn_pool_create(pool);
svn_delta_editor_t *tree_editor = svn_delta_default_editor(subpool);
const svn_wc_entry_t *entry;
SVN_ERR(svn_wc_entry(&entry, anchor, adm_access, FALSE, pool));
if (switch_url && entry && entry->repos &&
! svn_path_is_ancestor(entry->repos, switch_url))
return svn_error_createf
(SVN_ERR_WC_INVALID_SWITCH, NULL,
_("'%s'\n"
"is not the same repository as\n"
"'%s'"), switch_url, entry->repos);
eb = apr_pcalloc(subpool, sizeof(*eb));
eb->pool = subpool;
eb->use_commit_times= use_commit_times;
eb->target_revision = target_revision;
eb->switch_url = switch_url;
eb->repos = entry ? entry->repos : NULL;
eb->adm_access = adm_access;
eb->anchor = anchor;
eb->target = target;
eb->recurse = recurse;
eb->notify_func = notify_func;
eb->notify_baton = notify_baton;
eb->traversal_info = traversal_info;
eb->diff3_cmd = diff3_cmd;
eb->cancel_func = cancel_func;
eb->cancel_baton = cancel_baton;
tree_editor->set_target_revision = set_target_revision;
tree_editor->open_root = open_root;
tree_editor->delete_entry = delete_entry;
tree_editor->add_directory = add_directory;
tree_editor->open_directory = open_directory;
tree_editor->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;
SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
cancel_baton,
tree_editor,
eb,
editor,
edit_baton,
pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_get_update_editor2(svn_revnum_t *target_revision,
svn_wc_adm_access_t *anchor,
const char *target,
svn_boolean_t use_commit_times,
svn_boolean_t recurse,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
const char *diff3_cmd,
const svn_delta_editor_t **editor,
void **edit_baton,
svn_wc_traversal_info_t *traversal_info,
apr_pool_t *pool)
{
return make_editor(target_revision, anchor, svn_wc_adm_access_path(anchor),
target, use_commit_times, NULL, recurse, notify_func,
notify_baton, cancel_func, cancel_baton, diff3_cmd,
editor, edit_baton, traversal_info, pool);
}
svn_error_t *
svn_wc_get_update_editor(svn_revnum_t *target_revision,
svn_wc_adm_access_t *anchor,
const char *target,
svn_boolean_t use_commit_times,
svn_boolean_t recurse,
svn_wc_notify_func_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
const char *diff3_cmd,
const svn_delta_editor_t **editor,
void **edit_baton,
svn_wc_traversal_info_t *traversal_info,
apr_pool_t *pool)
{
svn_wc__compat_notify_baton_t *nb = apr_palloc(pool, sizeof(*nb));
nb->func = notify_func;
nb->baton = notify_baton;
return svn_wc_get_update_editor2(target_revision, anchor, target,
use_commit_times, recurse,
svn_wc__compat_call_notify_func, nb,
cancel_func, cancel_baton, diff3_cmd,
editor, edit_baton, traversal_info, pool);
}
svn_error_t *
svn_wc_get_switch_editor2(svn_revnum_t *target_revision,
svn_wc_adm_access_t *anchor,
const char *target,
const char *switch_url,
svn_boolean_t use_commit_times,
svn_boolean_t recurse,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
const char *diff3_cmd,
const svn_delta_editor_t **editor,
void **edit_baton,
svn_wc_traversal_info_t *traversal_info,
apr_pool_t *pool)
{
assert(switch_url);
return make_editor(target_revision, anchor, svn_wc_adm_access_path(anchor),
target, use_commit_times, switch_url, recurse,
notify_func, notify_baton, cancel_func, cancel_baton,
diff3_cmd, editor, edit_baton, traversal_info, pool);
}
svn_error_t *
svn_wc_get_switch_editor(svn_revnum_t *target_revision,
svn_wc_adm_access_t *anchor,
const char *target,
const char *switch_url,
svn_boolean_t use_commit_times,
svn_boolean_t recurse,
svn_wc_notify_func_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
const char *diff3_cmd,
const svn_delta_editor_t **editor,
void **edit_baton,
svn_wc_traversal_info_t *traversal_info,
apr_pool_t *pool)
{
svn_wc__compat_notify_baton_t *nb = apr_palloc(pool, sizeof(*nb));
nb->func = notify_func;
nb->baton = notify_baton;
return svn_wc_get_switch_editor2(target_revision, anchor, target,
switch_url, use_commit_times, recurse,
svn_wc__compat_call_notify_func, nb,
cancel_func, cancel_baton, diff3_cmd,
editor, edit_baton, traversal_info, pool);
}
svn_wc_traversal_info_t *
svn_wc_init_traversal_info(apr_pool_t *pool)
{
svn_wc_traversal_info_t *ti = apr_palloc(pool, sizeof(*ti));
ti->pool = pool;
ti->externals_old = apr_hash_make(pool);
ti->externals_new = apr_hash_make(pool);
return ti;
}
void
svn_wc_edited_externals(apr_hash_t **externals_old,
apr_hash_t **externals_new,
svn_wc_traversal_info_t *traversal_info)
{
*externals_old = traversal_info->externals_old;
*externals_new = traversal_info->externals_new;
}
static svn_error_t *
check_wc_root(svn_boolean_t *wc_root,
svn_node_kind_t *kind,
const char *path,
svn_wc_adm_access_t *adm_access,
apr_pool_t *pool)
{
const char *parent, *base_name;
const svn_wc_entry_t *p_entry, *entry;
svn_error_t *err;
svn_wc_adm_access_t *p_access;
*wc_root = TRUE;
SVN_ERR(svn_wc_entry(&entry, path, adm_access, FALSE, pool));
if (kind)
*kind = entry ? entry->kind : svn_node_file;
if (svn_path_is_empty(path))
return SVN_NO_ERROR;
p_entry = NULL;
svn_path_split(path, &parent, &base_name, pool);
SVN_ERR(svn_wc__adm_retrieve_internal(&p_access, adm_access, parent,
pool));
err = SVN_NO_ERROR;
if (! p_access)
err = svn_wc_adm_probe_open3(&p_access, NULL, parent, FALSE, 0,
NULL, NULL, pool);
if (! err)
err = svn_wc_entry(&p_entry, parent, p_access, FALSE, pool);
if (err || (! p_entry))
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
if (! p_entry->url)
return svn_error_createf
(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' has no ancestry information"),
svn_path_local_style(parent, pool));
if (entry && entry->url
&& (strcmp(svn_path_url_add_component(p_entry->url, base_name, pool),
entry->url) != 0))
return SVN_NO_ERROR;
SVN_ERR(svn_wc_entry(&p_entry, path, p_access, FALSE, pool));
if (! p_entry)
return SVN_NO_ERROR;
*wc_root = FALSE;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_is_wc_root(svn_boolean_t *wc_root,
const char *path,
svn_wc_adm_access_t *adm_access,
apr_pool_t *pool)
{
return check_wc_root(wc_root, NULL, path, adm_access, pool);
}
svn_error_t *
svn_wc_get_actual_target(const char *path,
const char **anchor,
const char **target,
apr_pool_t *pool)
{
svn_wc_adm_access_t *adm_access;
svn_boolean_t is_wc_root;
svn_node_kind_t kind;
SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, path, FALSE, 0,
NULL, NULL, pool));
SVN_ERR(check_wc_root(&is_wc_root, &kind, path, adm_access, pool));
SVN_ERR(svn_wc_adm_close(adm_access));
if ((! is_wc_root) || (kind == svn_node_file))
{
svn_path_split(path, anchor, target, pool);
}
else
{
*anchor = apr_pstrdup(pool, path);
*target = "";
}
return SVN_NO_ERROR;
}
static svn_error_t *
install_added_props(svn_stringbuf_t *log_accum,
svn_wc_adm_access_t *adm_access,
const char *dst_path,
apr_hash_t *new_base_props,
apr_hash_t *new_props,
apr_pool_t *pool)
{
apr_array_header_t *regular_props = NULL, *wc_props = NULL,
*entry_props = NULL;
const char *base_name = svn_path_basename(dst_path, pool);
{
apr_array_header_t *prop_array;
int i;
SVN_ERR(svn_prop_diffs(&prop_array, new_base_props,
apr_hash_make(pool), pool));
SVN_ERR(svn_categorize_props(prop_array,
&entry_props, &wc_props, ®ular_props,
pool));
new_base_props = apr_hash_make(pool);
for (i = 0; i < regular_props->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(regular_props, i, svn_prop_t);
apr_hash_set(new_base_props, prop->name, APR_HASH_KEY_STRING,
prop->value);
}
}
SVN_ERR(svn_wc__install_props(&log_accum, adm_access, base_name,
new_base_props,
new_props ? new_props : new_base_props,
TRUE, pool));
SVN_ERR(accumulate_entry_props(log_accum, NULL,
adm_access, base_name,
entry_props, pool));
SVN_ERR(accumulate_wcprops(log_accum, adm_access,
base_name, wc_props, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_add_repos_file2(const char *dst_path,
svn_wc_adm_access_t *adm_access,
const char *new_text_base_path,
const char *new_text_path,
apr_hash_t *new_base_props,
apr_hash_t *new_props,
const char *copyfrom_url,
svn_revnum_t copyfrom_rev,
apr_pool_t *pool)
{
const char *new_URL;
const char *adm_path = svn_wc_adm_access_path(adm_access);
const char *tmp_text_base_path =
svn_wc__text_base_path(dst_path, TRUE, pool);
const char *local_tmp_text_base_path =
svn_path_is_child(adm_path, tmp_text_base_path, pool);
const char *text_base_path =
svn_wc__text_base_path(dst_path, FALSE, pool);
const char *local_text_base_path =
svn_path_is_child(adm_path, text_base_path, pool);
const svn_wc_entry_t *ent;
const svn_wc_entry_t *dst_entry;
svn_stringbuf_t *log_accum;
const char *dir_name, *base_name;
svn_path_split(dst_path, &dir_name, &base_name, pool);
{
SVN_ERR(svn_wc_entry(&ent, dir_name, adm_access, FALSE, pool));
if (! ent)
return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
_("'%s' is not under version control"),
svn_path_local_style(dir_name, pool));
new_URL = svn_path_url_add_component(ent->url, base_name, pool);
if (copyfrom_url && ent->repos &&
! svn_path_is_ancestor(ent->repos, copyfrom_url))
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Copyfrom-url '%s' has different repository"
" root than '%s'"),
copyfrom_url, ent->repos);
}
log_accum = svn_stringbuf_create("", pool);
SVN_ERR(svn_wc_entry(&dst_entry, dst_path, adm_access, FALSE, pool));
if (dst_entry && dst_entry->schedule == svn_wc_schedule_delete)
{
const char *full_path = svn_wc_adm_access_path(adm_access);
const char *dst_rtext = svn_wc__text_revert_path(base_name, FALSE,
pool);
const char *dst_txtb = svn_wc__text_base_path(base_name, FALSE, pool);
const char *dst_rprop;
const char *dst_bprop;
svn_node_kind_t kind;
SVN_ERR(svn_wc__prop_revert_path(&dst_rprop, base_name,
svn_node_file, FALSE, pool));
SVN_ERR(svn_wc__prop_base_path(&dst_bprop, base_name,
svn_node_file, FALSE, pool));
SVN_ERR(svn_wc__loggy_move(&log_accum, NULL,
adm_access, dst_txtb, dst_rtext,
FALSE, pool));
SVN_ERR(svn_io_check_path(svn_path_join(full_path, dst_bprop, pool),
&kind, pool));
if (kind == svn_node_file)
SVN_ERR(svn_wc__loggy_move(&log_accum, NULL,
adm_access, dst_bprop, dst_rprop,
FALSE, pool));
}
{
svn_wc_entry_t tmp_entry;
apr_uint32_t modify_flags = SVN_WC__ENTRY_MODIFY_SCHEDULE;
tmp_entry.schedule = svn_wc_schedule_add;
if (copyfrom_url)
{
assert(SVN_IS_VALID_REVNUM(copyfrom_rev));
tmp_entry.copyfrom_url = copyfrom_url;
tmp_entry.copyfrom_rev = copyfrom_rev;
tmp_entry.copied = TRUE;
modify_flags |= SVN_WC__ENTRY_MODIFY_COPYFROM_URL
| SVN_WC__ENTRY_MODIFY_COPYFROM_REV
| SVN_WC__ENTRY_MODIFY_COPIED;
}
SVN_ERR(svn_wc__loggy_entry_modify(&log_accum, adm_access,
base_name, &tmp_entry,
modify_flags, pool));
}
SVN_ERR(tweak_entry(log_accum, adm_access, base_name,
dst_entry ? dst_entry->revision : ent->revision,
new_URL, pool));
SVN_ERR(install_added_props(log_accum, adm_access, dst_path,
new_base_props, new_props, pool));
if (strcmp(tmp_text_base_path, new_text_base_path) != 0)
SVN_ERR(svn_io_file_move(new_text_base_path, tmp_text_base_path,
pool));
if (new_text_path)
{
const char *tmp_text_path;
const char *local_tmp_text_path;
SVN_ERR(svn_wc_create_tmp_file2(NULL, &tmp_text_path, adm_path,
svn_io_file_del_none, pool));
SVN_ERR(svn_io_file_move(new_text_path, tmp_text_path, pool));
local_tmp_text_path = svn_path_is_child(adm_path, tmp_text_path, pool);
if (svn_wc__has_special_property(new_base_props))
{
SVN_ERR(svn_wc__loggy_copy(&log_accum, NULL, adm_access,
svn_wc__copy_translate_special_only,
local_tmp_text_path,
base_name, FALSE, pool));
SVN_ERR(svn_wc__loggy_remove(&log_accum, adm_access,
local_tmp_text_path, pool));
}
else
SVN_ERR(svn_wc__loggy_move(&log_accum, NULL, adm_access,
local_tmp_text_path, base_name,
FALSE, pool));
SVN_ERR(svn_wc__loggy_maybe_set_readonly(&log_accum, adm_access,
base_name, pool));
}
else
{
SVN_ERR(svn_wc__loggy_copy(&log_accum, NULL, adm_access,
svn_wc__copy_translate,
local_tmp_text_base_path, base_name, FALSE,
pool));
SVN_ERR(svn_wc__loggy_set_entry_timestamp_from_wc
(&log_accum, adm_access,
base_name, SVN_WC__ENTRY_ATTR_TEXT_TIME, pool));
}
{
unsigned char digest[APR_MD5_DIGESTSIZE];
svn_wc_entry_t tmp_entry;
SVN_ERR(svn_wc__loggy_move(&log_accum, NULL,
adm_access, local_tmp_text_base_path,
local_text_base_path, FALSE, pool));
SVN_ERR(svn_wc__loggy_set_readonly(&log_accum, adm_access,
local_text_base_path, pool));
SVN_ERR(svn_io_file_checksum(digest, tmp_text_base_path, pool));
tmp_entry.checksum = svn_md5_digest_to_cstring(digest, pool);
SVN_ERR(svn_wc__loggy_entry_modify(&log_accum, adm_access,
base_name, &tmp_entry,
SVN_WC__ENTRY_MODIFY_CHECKSUM,
pool));
}
SVN_ERR(svn_wc__write_log(adm_access, 0, log_accum, pool));
SVN_ERR(svn_wc__run_log(adm_access, NULL, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_add_repos_file(const char *dst_path,
svn_wc_adm_access_t *adm_access,
const char *new_text_path,
apr_hash_t *new_props,
const char *copyfrom_url,
svn_revnum_t copyfrom_rev,
apr_pool_t *pool)
{
return svn_wc_add_repos_file2(dst_path, adm_access,
new_text_path, NULL,
new_props, NULL,
copyfrom_url, copyfrom_rev,
pool);
}