You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2012/05/16 22:32:54 UTC
svn commit: r1339349 [11/37] - in /subversion/branches/fix-rdump-editor: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/emacs/ contrib/client-side/vim/
contrib/server-side/ notes/ notes/api-errat...
Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_delta/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_delta/compat.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_delta/compat.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_delta/compat.c Wed May 16 20:32:43 2012
@@ -21,6 +21,8 @@
* ====================================================================
*/
+#include <stddef.h>
+
#include "svn_types.h"
#include "svn_error.h"
#include "svn_delta.h"
@@ -31,7 +33,11 @@
#include "svn_props.h"
#include "svn_pools.h"
-
+#include "svn_private_config.h"
+
+#include "private/svn_delta_private.h"
+
+
struct file_rev_handler_wrapper_baton {
void *baton;
svn_file_rev_handler_old_t handler;
@@ -104,38 +110,21 @@ svn_compat_wrap_file_rev_handler(svn_fil
* details.
*/
-typedef svn_error_t *(*start_edit_func_t)(
- void *baton,
- svn_revnum_t base_revision);
-
-typedef svn_error_t *(*target_revision_func_t)(
- void *baton,
- svn_revnum_t target_revision,
- apr_pool_t *scratch_pool);
-
-typedef svn_error_t *(*unlock_func_t)(
- void *baton,
- const char *path,
- apr_pool_t *scratch_pool);
-
-/* See svn_editor__insert_shims() for more information. */
-struct extra_baton
-{
- start_edit_func_t start_edit;
- target_revision_func_t target_revision;
- void *baton;
-};
-
struct ev2_edit_baton
{
svn_editor_t *editor;
- apr_hash_t *paths;
+ apr_hash_t *changes; /* REPOS_RELPATH -> struct change_node */
+
apr_array_header_t *path_order;
int paths_processed;
+ /* For calculating relpaths from Ev1 copyfrom urls. */
+ const char *repos_root;
+ const char *base_relpath;
+
apr_pool_t *edit_pool;
- struct extra_baton *exb;
+ struct svn_delta__extra_baton *exb;
svn_boolean_t closed;
svn_boolean_t *found_abs_paths; /* Did we strip an incoming '/' from the
@@ -147,7 +136,7 @@ struct ev2_edit_baton
svn_delta_fetch_base_func_t fetch_base_func;
void *fetch_base_baton;
- unlock_func_t do_unlock;
+ svn_delta__unlock_func_t do_unlock;
void *unlock_baton;
};
@@ -157,7 +146,7 @@ struct ev2_dir_baton
const char *path;
svn_revnum_t base_revision;
- const char *copyfrom_path;
+ const char *copyfrom_relpath;
svn_revnum_t copyfrom_rev;
};
@@ -169,75 +158,128 @@ struct ev2_file_baton
const char *delta_base;
};
-enum action_code_t
+enum restructure_action_t
{
- ACTION_MOVE,
- ACTION_MKDIR,
- ACTION_COPY,
- ACTION_PROPSET,
- ACTION_PUT,
- ACTION_ADD,
- ACTION_DELETE,
- ACTION_ADD_ABSENT,
- ACTION_SET_TEXT,
- ACTION_UNLOCK
+ RESTRUCTURE_NONE = 0,
+ RESTRUCTURE_ADD, /* add the node, maybe replacing. maybe copy */
+ RESTRUCTURE_ADD_ABSENT, /* add an absent node, possibly replacing */
+ RESTRUCTURE_DELETE, /* delete this node */
};
-struct path_action
+/* Records everything about how this node is to be changed. */
+struct change_node
{
- enum action_code_t action;
- void *args;
-};
+ /* what kind of (tree) restructure is occurring at this node? */
+ enum restructure_action_t action;
-struct prop_args
-{
- const char *name;
- svn_revnum_t base_revision;
- const svn_string_t *value;
- svn_kind_t kind;
-};
+ svn_kind_t kind; /* the NEW kind of this node */
-struct copy_args
-{
+ /* We need two revisions: one to specify the revision we are altering,
+ and a second to specify the revision to delete/replace. These are
+ mutually exclusive, but they need to be separate to ensure we don't
+ confuse the operation on this node. For example, we may delete a
+ node and replace it we use DELETING for REPLACES_REV, and ignore
+ the value placed into CHANGING when properties were set/changed
+ on the new node. Or we simply change a node (setting CHANGING),
+ and DELETING remains SVN_INVALID_REVNUM, indicating we are not
+ attempting to replace a node. */
+ svn_revnum_t changing;
+ svn_revnum_t deleting;
+
+ apr_hash_t *props; /* new/final set of props to apply */
+
+ const char *contents_abspath; /* file containing new fulltext */
+ svn_checksum_t *checksum; /* checksum of new fulltext */
+
+ /* If COPYFROM_PATH is not NULL, then copy PATH@REV to this node.
+ RESTRUCTURE must be RESTRUCTURE_ADD. */
const char *copyfrom_path;
svn_revnum_t copyfrom_rev;
+
+ /* Record whether an incoming propchange unlocked this node. */
+ svn_boolean_t unlock;
};
-struct path_checksum_args
+
+static struct change_node *
+locate_change(struct ev2_edit_baton *eb,
+ const char *relpath)
{
- const char *path;
- svn_revnum_t base_revision;
-};
+ struct change_node *change = apr_hash_get(eb->changes, relpath,
+ APR_HASH_KEY_STRING);
+
+ if (change != NULL)
+ return change;
+
+ /* Shift RELPATH into the proper pool, and record the observed order. */
+ relpath = apr_pstrdup(eb->edit_pool, relpath);
+ APR_ARRAY_PUSH(eb->path_order, const char *) = relpath;
+
+ /* Return an empty change. Callers will tweak as needed. */
+ change = apr_pcalloc(eb->edit_pool, sizeof(*change));
+ change->changing = SVN_INVALID_REVNUM;
+ change->deleting = SVN_INVALID_REVNUM;
+
+ apr_hash_set(eb->changes, relpath, APR_HASH_KEY_STRING, change);
+
+ return change;
+}
+
static svn_error_t *
-add_action(struct ev2_edit_baton *eb,
- const char *path,
- enum action_code_t action,
- void *args)
+apply_propedit(struct ev2_edit_baton *eb,
+ const char *relpath,
+ svn_kind_t kind,
+ svn_revnum_t base_revision,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool)
{
- struct path_action *p_action;
- apr_array_header_t *action_list = apr_hash_get(eb->paths, path,
- APR_HASH_KEY_STRING);
+ struct change_node *change = locate_change(eb, relpath);
- p_action = apr_palloc(eb->edit_pool, sizeof(*p_action));
- p_action->action = action;
- p_action->args = args;
+ SVN_ERR_ASSERT(change->kind == svn_kind_unknown || change->kind == kind);
+ change->kind = kind;
- if (action_list == NULL)
- {
- const char *path_dup = apr_pstrdup(eb->edit_pool, path);
-
- action_list = apr_array_make(eb->edit_pool, 1,
- sizeof(struct path_action *));
- apr_hash_set(eb->paths, path_dup, APR_HASH_KEY_STRING, action_list);
- APR_ARRAY_PUSH(eb->path_order, const char *) = path_dup;
+ /* We're now changing the node. Record the revision. */
+ SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->changing)
+ || change->changing == base_revision);
+ change->changing = base_revision;
+
+ if (change->props == NULL)
+ {
+ /* Fetch the original set of properties. We'll apply edits to create
+ the new/target set of properties.
+
+ If this is a copied/moved now, then the original properties come
+ from there. If the node has been added, it starts with empty props.
+ Otherwise, we get the properties from BASE. */
+
+ if (change->copyfrom_path)
+ SVN_ERR(eb->fetch_props_func(&change->props,
+ eb->fetch_props_baton,
+ change->copyfrom_path,
+ change->copyfrom_rev,
+ eb->edit_pool, scratch_pool));
+ else if (change->action == RESTRUCTURE_ADD)
+ change->props = apr_hash_make(eb->edit_pool);
+ else
+ SVN_ERR(eb->fetch_props_func(&change->props,
+ eb->fetch_props_baton,
+ relpath, base_revision,
+ eb->edit_pool, scratch_pool));
}
- APR_ARRAY_PUSH(action_list, struct path_action *) = p_action;
+ if (value == NULL)
+ apr_hash_set(change->props, name, APR_HASH_KEY_STRING, NULL);
+ else
+ apr_hash_set(change->props,
+ apr_pstrdup(eb->edit_pool, name), APR_HASH_KEY_STRING,
+ svn_string_dup(value, eb->edit_pool));
return SVN_NO_ERROR;
}
+
/* Find all the paths which are immediate children of PATH and return their
basenames in a list. */
static apr_array_header_t *
@@ -248,17 +290,13 @@ get_children(struct ev2_edit_baton *eb,
apr_array_header_t *children = apr_array_make(pool, 1, sizeof(const char *));
apr_hash_index_t *hi;
- for (hi = apr_hash_first(pool, eb->paths); hi; hi = apr_hash_next(hi))
+ for (hi = apr_hash_first(pool, eb->changes); hi; hi = apr_hash_next(hi))
{
- const char *p = svn__apr_hash_index_key(hi);
+ const char *repos_relpath = svn__apr_hash_index_key(hi);
const char *child;
- /* Sanitize our paths. */
- if (*p == '/')
- p++;
-
/* Find potential children. */
- child = svn_relpath_skip_ancestor(path, p);
+ child = svn_relpath_skip_ancestor(path, repos_relpath);
if (!child || !*child)
continue;
@@ -275,210 +313,134 @@ get_children(struct ev2_edit_baton *eb,
static svn_error_t *
-process_actions(void *edit_baton,
- const char *path,
- apr_array_header_t *actions,
+process_actions(struct ev2_edit_baton *eb,
+ const char *repos_relpath,
+ const struct change_node *change,
apr_pool_t *scratch_pool)
{
- struct ev2_edit_baton *eb = edit_baton;
apr_hash_t *props = NULL;
- svn_boolean_t need_add = FALSE;
- svn_boolean_t need_delete = FALSE;
- svn_boolean_t need_copy = FALSE;
- const char *copyfrom_path;
- svn_revnum_t copyfrom_rev;
- apr_array_header_t *children = NULL;
svn_stream_t *contents = NULL;
svn_checksum_t *checksum = NULL;
- svn_revnum_t delete_revnum = SVN_INVALID_REVNUM;
- svn_revnum_t props_base_revision = SVN_INVALID_REVNUM;
- svn_revnum_t text_base_revision = SVN_INVALID_REVNUM;
svn_kind_t kind = svn_kind_unknown;
- int i;
- if (*path == '/')
+ SVN_ERR_ASSERT(change != NULL);
+
+ if (change->unlock)
+ SVN_ERR(eb->do_unlock(eb->unlock_baton, repos_relpath, scratch_pool));
+
+ if (change->action == RESTRUCTURE_DELETE)
{
- path++;
- *eb->found_abs_paths = TRUE;
+ /* If the action was left as RESTRUCTURE_DELETE, then a
+ replacement is not occurring. Just do the delete and bail. */
+ SVN_ERR(svn_editor_delete(eb->editor, repos_relpath,
+ change->deleting));
+
+ /* No further work possible on this node. */
+ return SVN_NO_ERROR;
}
+ if (change->action == RESTRUCTURE_ADD_ABSENT)
+ {
+ SVN_ERR(svn_editor_add_absent(eb->editor, repos_relpath,
+ change->kind, change->deleting));
- /* Go through all of our actions, populating various datastructures
- * dependent on them. */
- for (i = 0; i < actions->nelts; i++)
+ /* No further work possible on this node. */
+ return SVN_NO_ERROR;
+ }
+
+ if (change->contents_abspath != NULL)
{
- const struct path_action *action = APR_ARRAY_IDX(actions, i,
- struct path_action *);
+ /* We can only set text on files. */
+ /* ### validate we aren't overwriting KIND? */
+ kind = svn_kind_file;
- switch (action->action)
- {
- case ACTION_PROPSET:
- {
- const struct prop_args *p_args = action->args;
+ /* ### the checksum might be in CHANGE->CHECKSUM */
+ SVN_ERR(svn_io_file_checksum2(&checksum, change->contents_abspath,
+ svn_checksum_sha1, scratch_pool));
+ SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath,
+ scratch_pool, scratch_pool));
+ }
- kind = p_args->kind;
+ if (change->props != NULL)
+ {
+ /* ### validate we aren't overwriting KIND? */
+ kind = change->kind;
+ props = change->props;
+ }
- if (!SVN_IS_VALID_REVNUM(props_base_revision))
- props_base_revision = p_args->base_revision;
- else
- SVN_ERR_ASSERT(p_args->base_revision == props_base_revision);
+ if (change->action == RESTRUCTURE_ADD)
+ {
+ /* An add might be a replace. Grab the revnum we're replacing. */
+ svn_revnum_t replaces_rev = change->deleting;
- if (!props)
- {
- /* Fetch the original props. We can then apply each of
- the modifications to it. */
- if (need_delete && need_add)
- props = apr_hash_make(scratch_pool);
- else if (need_copy)
- SVN_ERR(eb->fetch_props_func(&props,
- eb->fetch_props_baton,
- copyfrom_path,
- copyfrom_rev,
- scratch_pool, scratch_pool));
- else
- SVN_ERR(eb->fetch_props_func(&props,
- eb->fetch_props_baton,
- path, props_base_revision,
- scratch_pool, scratch_pool));
- }
+ kind = change->kind;
- /* Note that p_args->value may be NULL. */
- apr_hash_set(props, p_args->name, APR_HASH_KEY_STRING,
- p_args->value);
- break;
- }
+ if (change->copyfrom_path != NULL)
+ {
+ SVN_ERR(svn_editor_copy(eb->editor, change->copyfrom_path,
+ change->copyfrom_rev,
+ repos_relpath, replaces_rev));
+ /* Fall through to possibly make changes post-copy. */
+ }
+ else
+ {
+ /* If no properties were defined, then use an empty set. */
+ if (props == NULL)
+ props = apr_hash_make(scratch_pool);
- case ACTION_DELETE:
+ if (kind == svn_kind_dir)
{
- delete_revnum = *((svn_revnum_t *) action->args);
- need_delete = TRUE;
- break;
- }
+ const apr_array_header_t *children;
- case ACTION_ADD:
+ children = get_children(eb, repos_relpath, scratch_pool);
+ SVN_ERR(svn_editor_add_directory(eb->editor, repos_relpath,
+ children, props,
+ replaces_rev));
+ }
+ else
{
- kind = *((svn_kind_t *) action->args);
- need_add = TRUE;
-
- if (kind == svn_kind_dir)
+ /* If this file was added, but apply_txdelta() was not
+ called (ie. no CONTENTS_ABSPATH), then we're adding
+ an empty file. */
+ if (change->contents_abspath == NULL)
{
- children = get_children(eb, path, scratch_pool);
- }
- else
- {
- /* The default is an empty file. */
contents = svn_stream_empty(scratch_pool);
checksum = svn_checksum_empty_checksum(svn_checksum_sha1,
scratch_pool);
}
- break;
- }
-
- case ACTION_SET_TEXT:
- {
- struct path_checksum_args *pca = action->args;
-
- /* We can only set text on files. */
- kind = svn_kind_file;
-
- SVN_ERR(svn_io_file_checksum2(&checksum, pca->path,
- svn_checksum_sha1, scratch_pool));
- SVN_ERR(svn_stream_open_readonly(&contents, pca->path,
- scratch_pool, scratch_pool));
-
- if (!SVN_IS_VALID_REVNUM(text_base_revision))
- text_base_revision = pca->base_revision;
- else
- SVN_ERR_ASSERT(pca->base_revision == text_base_revision);
-
- break;
- }
-
- case ACTION_COPY:
- {
- struct copy_args *c_args = action->args;
-
- copyfrom_path = c_args->copyfrom_path;
- copyfrom_rev = c_args->copyfrom_rev;
- need_copy = TRUE;
- break;
- }
- case ACTION_ADD_ABSENT:
- {
- kind = *((svn_kind_t *) action->args);
- SVN_ERR(svn_editor_add_absent(eb->editor, path, kind,
- SVN_INVALID_REVNUM));
- break;
- }
-
- case ACTION_UNLOCK:
- {
- SVN_ERR(eb->do_unlock(eb->unlock_baton, path, scratch_pool));
- break;
+ SVN_ERR(svn_editor_add_file(eb->editor, repos_relpath,
+ checksum, contents, props,
+ replaces_rev));
}
- default:
- SVN_ERR_MALFUNCTION();
+ /* No further work possible on this node. */
+ return SVN_NO_ERROR;
}
}
- /* We've now got a wholistic view of what has happened to this node,
- * so we can call our own editor APIs on it. */
-
- if (need_delete && !need_add && !need_copy)
- {
- /* If we're only doing a delete, do it here. */
- SVN_ERR(svn_editor_delete(eb->editor, path, delete_revnum));
- return SVN_NO_ERROR;
- }
-
- if (need_add)
+#if 0
+ /* There *should* be work for this node. But it seems that isn't true
+ in some cases. Future investigation... */
+ SVN_ERR_ASSERT(props || contents);
+#endif
+ if (props || contents)
{
- if (props == NULL)
- props = apr_hash_make(scratch_pool);
+ /* Changes to properties or content should have indicated the revision
+ it was intending to change.
- if (kind == svn_kind_dir)
- {
- SVN_ERR(svn_editor_add_directory(eb->editor, path, children,
- props, delete_revnum));
- }
- else
- {
- SVN_ERR(svn_editor_add_file(eb->editor, path, checksum, contents,
- props, delete_revnum));
- }
-
- return SVN_NO_ERROR;
- }
-
- if (need_copy)
- {
- SVN_ERR(svn_editor_copy(eb->editor, copyfrom_path, copyfrom_rev, path,
- delete_revnum));
- }
+ Oop. Not true. The node may be locally-added. */
+#if 0
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(change->changing));
+#endif
- if (props || contents)
- {
- /* We fetched and modified the props or content in some way. Apply 'em
- now. */
- svn_revnum_t base_revision;
-
- if (SVN_IS_VALID_REVNUM(props_base_revision)
- && SVN_IS_VALID_REVNUM(text_base_revision))
- SVN_ERR_ASSERT(props_base_revision == text_base_revision);
-
- if (SVN_IS_VALID_REVNUM(props_base_revision))
- base_revision = props_base_revision;
- else if (SVN_IS_VALID_REVNUM(text_base_revision))
- base_revision = text_base_revision;
- else
- base_revision = SVN_INVALID_REVNUM;
+ /* ### we need to gather up the target set of children */
if (kind == svn_kind_dir)
- SVN_ERR(svn_editor_alter_directory(eb->editor, path, base_revision,
- props));
+ SVN_ERR(svn_editor_alter_directory(eb->editor, repos_relpath,
+ change->changing, NULL, props));
else
- SVN_ERR(svn_editor_alter_file(eb->editor, path, base_revision, props,
+ SVN_ERR(svn_editor_alter_file(eb->editor, repos_relpath,
+ change->changing, props,
checksum, contents));
}
@@ -486,10 +448,9 @@ process_actions(void *edit_baton,
}
static svn_error_t *
-run_ev2_actions(void *edit_baton,
+run_ev2_actions(struct ev2_edit_baton *eb,
apr_pool_t *scratch_pool)
{
- struct ev2_edit_baton *eb = edit_baton;
apr_pool_t *iterpool;
iterpool = svn_pool_create(scratch_pool);
@@ -498,19 +459,42 @@ run_ev2_actions(void *edit_baton,
as part of close_edit() and then some more as part of abort_edit() */
for (; eb->paths_processed < eb->path_order->nelts; ++eb->paths_processed)
{
- const char *path = APR_ARRAY_IDX(eb->path_order, eb->paths_processed,
- const char *);
- apr_array_header_t *actions = apr_hash_get(eb->paths, path,
- APR_HASH_KEY_STRING);
+ const char *repos_relpath = APR_ARRAY_IDX(eb->path_order,
+ eb->paths_processed,
+ const char *);
+ const struct change_node *change = apr_hash_get(eb->changes,
+ repos_relpath,
+ APR_HASH_KEY_STRING);
svn_pool_clear(iterpool);
- SVN_ERR(process_actions(edit_baton, path, actions, iterpool));
+
+ SVN_ERR(process_actions(eb, repos_relpath, change, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
+
+static const char *
+map_to_repos_relpath(struct ev2_edit_baton *eb,
+ const char *path_or_url,
+ apr_pool_t *result_pool)
+{
+ if (svn_path_is_url(path_or_url))
+ {
+ return svn_uri_skip_ancestor(eb->repos_root, path_or_url, result_pool);
+ }
+ else
+ {
+ return svn_relpath_join(eb->base_relpath,
+ path_or_url[0] == '/'
+ ? path_or_url + 1 : path_or_url,
+ result_pool);
+ }
+}
+
+
static svn_error_t *
ev2_set_target_revision(void *edit_baton,
svn_revnum_t target_revision,
@@ -535,7 +519,7 @@ ev2_open_root(void *edit_baton,
struct ev2_edit_baton *eb = edit_baton;
db->eb = eb;
- db->path = "";
+ db->path = apr_pstrdup(eb->edit_pool, eb->base_relpath);
db->base_revision = base_revision;
*root_baton = db;
@@ -553,10 +537,21 @@ ev2_delete_entry(const char *path,
apr_pool_t *scratch_pool)
{
struct ev2_dir_baton *pb = parent_baton;
- svn_revnum_t *revnum = apr_palloc(pb->eb->edit_pool, sizeof(*revnum));
+ svn_revnum_t base_revision;
+ const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
+ struct change_node *change = locate_change(pb->eb, relpath);
+
+ if (SVN_IS_VALID_REVNUM(revision))
+ base_revision = revision;
+ else
+ base_revision = pb->base_revision;
+
+ SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
+ change->action = RESTRUCTURE_DELETE;
- *revnum = revision;
- SVN_ERR(add_action(pb->eb, path, ACTION_DELETE, revnum));
+ SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->deleting)
+ || change->deleting == base_revision);
+ change->deleting = base_revision;
return SVN_NO_ERROR;
}
@@ -569,41 +564,42 @@ ev2_add_directory(const char *path,
apr_pool_t *result_pool,
void **child_baton)
{
+ /* ### fix this? */
+ apr_pool_t *scratch_pool = result_pool;
struct ev2_dir_baton *pb = parent_baton;
struct ev2_dir_baton *cb = apr_pcalloc(result_pool, sizeof(*cb));
+ const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
+ struct change_node *change = locate_change(pb->eb, relpath);
+
+ /* ### assert that RESTRUCTURE is NONE or DELETE? */
+ change->action = RESTRUCTURE_ADD;
+ change->kind = svn_kind_dir;
cb->eb = pb->eb;
- cb->path = apr_pstrdup(result_pool, path);
+ cb->path = apr_pstrdup(result_pool, relpath);
cb->base_revision = pb->base_revision;
*child_baton = cb;
if (!copyfrom_path)
{
- /* A simple add. */
- svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind));
-
- *kind = svn_kind_dir;
- SVN_ERR(add_action(pb->eb, path, ACTION_ADD, kind));
-
- if (pb->copyfrom_path)
+ if (pb->copyfrom_relpath)
{
- const char *name = svn_relpath_basename(path, result_pool);
- cb->copyfrom_path = apr_pstrcat(result_pool, pb->copyfrom_path,
- "/", name, NULL);
+ const char *name = svn_relpath_basename(relpath, scratch_pool);
+ cb->copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath, name,
+ result_pool);
cb->copyfrom_rev = pb->copyfrom_rev;
}
}
else
{
/* A copy */
- struct copy_args *args = apr_palloc(pb->eb->edit_pool, sizeof(*args));
- args->copyfrom_path = apr_pstrdup(pb->eb->edit_pool, copyfrom_path);
- args->copyfrom_rev = copyfrom_revision;
- SVN_ERR(add_action(pb->eb, path, ACTION_COPY, args));
+ change->copyfrom_path = map_to_repos_relpath(pb->eb, copyfrom_path,
+ pb->eb->edit_pool);
+ change->copyfrom_rev = copyfrom_revision;
- cb->copyfrom_path = args->copyfrom_path;
- cb->copyfrom_rev = args->copyfrom_rev;
+ cb->copyfrom_relpath = change->copyfrom_path;
+ cb->copyfrom_rev = change->copyfrom_rev;
}
return SVN_NO_ERROR;
@@ -616,20 +612,23 @@ ev2_open_directory(const char *path,
apr_pool_t *result_pool,
void **child_baton)
{
+ /* ### fix this? */
+ apr_pool_t *scratch_pool = result_pool;
struct ev2_dir_baton *pb = parent_baton;
struct ev2_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db));
+ const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
db->eb = pb->eb;
- db->path = apr_pstrdup(result_pool, path);
+ db->path = apr_pstrdup(result_pool, relpath);
db->base_revision = base_revision;
- if (pb->copyfrom_path)
+ if (pb->copyfrom_relpath)
{
/* We are inside a copy. */
- const char *name = svn_relpath_basename(path, result_pool);
+ const char *name = svn_relpath_basename(relpath, scratch_pool);
- db->copyfrom_path = apr_pstrcat(result_pool, pb->copyfrom_path,
- "/", name, NULL);
+ db->copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath, name,
+ result_pool);
db->copyfrom_rev = pb->copyfrom_rev;
}
@@ -644,14 +643,9 @@ ev2_change_dir_prop(void *dir_baton,
apr_pool_t *scratch_pool)
{
struct ev2_dir_baton *db = dir_baton;
- struct prop_args *p_args = apr_palloc(db->eb->edit_pool, sizeof(*p_args));
- p_args->name = apr_pstrdup(db->eb->edit_pool, name);
- p_args->value = value ? svn_string_dup(value, db->eb->edit_pool) : NULL;
- p_args->base_revision = db->base_revision;
- p_args->kind = svn_kind_dir;
-
- SVN_ERR(add_action(db->eb, db->path, ACTION_PROPSET, p_args));
+ SVN_ERR(apply_propedit(db->eb, db->path, svn_kind_dir, db->base_revision,
+ name, value, scratch_pool));
return SVN_NO_ERROR;
}
@@ -669,10 +663,12 @@ ev2_absent_directory(const char *path,
apr_pool_t *scratch_pool)
{
struct ev2_dir_baton *pb = parent_baton;
- svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind));
+ const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
+ struct change_node *change = locate_change(pb->eb, relpath);
- *kind = svn_kind_dir;
- SVN_ERR(add_action(pb->eb, path, ACTION_ADD_ABSENT, kind));
+ /* ### assert that RESTRUCTURE is NONE or DELETE? */
+ change->action = RESTRUCTURE_ADD_ABSENT;
+ change->kind = svn_kind_dir;
return SVN_NO_ERROR;
}
@@ -685,38 +681,40 @@ ev2_add_file(const char *path,
apr_pool_t *result_pool,
void **file_baton)
{
+ /* ### fix this? */
+ apr_pool_t *scratch_pool = result_pool;
struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
struct ev2_dir_baton *pb = parent_baton;
+ const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
+ struct change_node *change = locate_change(pb->eb, relpath);
+
+ /* ### assert that RESTRUCTURE is NONE or DELETE? */
+ change->action = RESTRUCTURE_ADD;
+ change->kind = svn_kind_file;
fb->eb = pb->eb;
- fb->path = apr_pstrdup(result_pool, path);
+ fb->path = apr_pstrdup(result_pool, relpath);
fb->base_revision = pb->base_revision;
*file_baton = fb;
if (!copyfrom_path)
{
- /* A simple add. */
- svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind));
-
/* Don't bother fetching the base, as in an add we don't have a base. */
fb->delta_base = NULL;
-
- *kind = svn_kind_file;
- SVN_ERR(add_action(pb->eb, path, ACTION_ADD, kind));
}
else
{
/* A copy */
- struct copy_args *args = apr_palloc(pb->eb->edit_pool, sizeof(*args));
+
+ change->copyfrom_path = map_to_repos_relpath(fb->eb, copyfrom_path,
+ fb->eb->edit_pool);
+ change->copyfrom_rev = copyfrom_revision;
SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
fb->eb->fetch_base_baton,
- copyfrom_path, copyfrom_revision,
- result_pool, result_pool));
-
- args->copyfrom_path = apr_pstrdup(pb->eb->edit_pool, copyfrom_path);
- args->copyfrom_rev = copyfrom_revision;
- SVN_ERR(add_action(pb->eb, path, ACTION_COPY, args));
+ change->copyfrom_path,
+ change->copyfrom_rev,
+ result_pool, scratch_pool));
}
return SVN_NO_ERROR;
@@ -729,32 +727,36 @@ ev2_open_file(const char *path,
apr_pool_t *result_pool,
void **file_baton)
{
+ /* ### fix this? */
+ apr_pool_t *scratch_pool = result_pool;
struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
struct ev2_dir_baton *pb = parent_baton;
+ const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
fb->eb = pb->eb;
- fb->path = apr_pstrdup(result_pool, path);
+ fb->path = apr_pstrdup(result_pool, relpath);
fb->base_revision = base_revision;
- if (pb->copyfrom_path)
+ if (pb->copyfrom_relpath)
{
/* We're in a copied directory, so the delta base is going to be
based up on the copy source. */
- const char *name = svn_relpath_basename(path, result_pool);
- const char *copyfrom_path = apr_pstrcat(result_pool, pb->copyfrom_path,
- "/", name, NULL);
+ const char *name = svn_relpath_basename(relpath, scratch_pool);
+ const char *copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath,
+ name,
+ scratch_pool);
SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
fb->eb->fetch_base_baton,
- copyfrom_path, pb->copyfrom_rev,
- result_pool, result_pool));
+ copyfrom_relpath, pb->copyfrom_rev,
+ result_pool, scratch_pool));
}
else
{
SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
fb->eb->fetch_base_baton,
- path, base_revision,
- result_pool, result_pool));
+ relpath, base_revision,
+ result_pool, scratch_pool));
}
*file_baton = fb;
@@ -799,21 +801,26 @@ ev2_apply_textdelta(void *file_baton,
struct ev2_file_baton *fb = file_baton;
apr_pool_t *handler_pool = svn_pool_create(fb->eb->edit_pool);
struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
+ struct change_node *change;
svn_stream_t *target;
- struct path_checksum_args *pca = apr_pcalloc(fb->eb->edit_pool,
- sizeof(*pca));
+ /* ### fix this. for now, we know this has a "short" lifetime. */
+ apr_pool_t *scratch_pool = handler_pool;
- pca->base_revision = fb->base_revision;
+ change = locate_change(fb->eb, fb->path);
+ SVN_ERR_ASSERT(change->contents_abspath == NULL);
+ SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->changing)
+ || change->changing == fb->base_revision);
+ change->changing = fb->base_revision;
if (! fb->delta_base)
hb->source = svn_stream_empty(handler_pool);
else
SVN_ERR(svn_stream_open_readonly(&hb->source, fb->delta_base, handler_pool,
- result_pool));
+ scratch_pool));
- SVN_ERR(svn_stream_open_unique(&target, &pca->path, NULL,
+ SVN_ERR(svn_stream_open_unique(&target, &change->contents_abspath, NULL,
svn_io_file_del_on_pool_cleanup,
- fb->eb->edit_pool, result_pool));
+ fb->eb->edit_pool, scratch_pool));
svn_txdelta_apply(hb->source, target,
NULL, NULL,
@@ -825,8 +832,6 @@ ev2_apply_textdelta(void *file_baton,
*handler_baton = hb;
*handler = window_handler;
- SVN_ERR(add_action(fb->eb, fb->path, ACTION_SET_TEXT, pca));
-
return SVN_NO_ERROR;
}
@@ -837,23 +842,22 @@ ev2_change_file_prop(void *file_baton,
apr_pool_t *scratch_pool)
{
struct ev2_file_baton *fb = file_baton;
- struct prop_args *p_args = apr_palloc(fb->eb->edit_pool, sizeof(*p_args));
if (!strcmp(name, SVN_PROP_ENTRY_LOCK_TOKEN) && value == NULL)
{
/* We special case the lock token propery deletion, which is the
server's way of telling the client to unlock the path. */
- SVN_ERR(add_action(fb->eb, fb->path, ACTION_UNLOCK, NULL));
- }
- /* We also pass through the deletion, since there may actually exist such
- a property we want to get rid of. In the worse case, this is a no-op. */
- p_args->name = apr_pstrdup(fb->eb->edit_pool, name);
- p_args->value = value ? svn_string_dup(value, fb->eb->edit_pool) : NULL;
- p_args->base_revision = fb->base_revision;
- p_args->kind = svn_kind_file;
+ /* ### this duplicates much of apply_propedit(). fix in future. */
+ const char *relpath = map_to_repos_relpath(fb->eb, fb->path,
+ scratch_pool);
+ struct change_node *change = locate_change(fb->eb, relpath);
+
+ change->unlock = TRUE;
+ }
- SVN_ERR(add_action(fb->eb, fb->path, ACTION_PROPSET, p_args));
+ SVN_ERR(apply_propedit(fb->eb, fb->path, svn_kind_file, fb->base_revision,
+ name, value, scratch_pool));
return SVN_NO_ERROR;
}
@@ -872,10 +876,12 @@ ev2_absent_file(const char *path,
apr_pool_t *scratch_pool)
{
struct ev2_dir_baton *pb = parent_baton;
- svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind));
+ const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
+ struct change_node *change = locate_change(pb->eb, relpath);
- *kind = svn_kind_file;
- SVN_ERR(add_action(pb->eb, path, ACTION_ADD_ABSENT, kind));
+ /* ### assert that RESTRUCTURE is NONE or DELETE? */
+ change->action = RESTRUCTURE_ADD_ABSENT;
+ change->kind = svn_kind_file;
return SVN_NO_ERROR;
}
@@ -924,18 +930,20 @@ ev2_abort_edit(void *edit_baton,
* Its callbacks should be invoked at the appropriate time by this
* shim.
*/
-static svn_error_t *
-delta_from_editor(const svn_delta_editor_t **deditor,
+svn_error_t *
+svn_delta__delta_from_editor(const svn_delta_editor_t **deditor,
void **dedit_baton,
svn_editor_t *editor,
- unlock_func_t unlock_func,
+ svn_delta__unlock_func_t unlock_func,
void *unlock_baton,
svn_boolean_t *found_abs_paths,
+ const char *repos_root,
+ const char *base_relpath,
svn_delta_fetch_props_func_t fetch_props_func,
void *fetch_props_baton,
svn_delta_fetch_base_func_t fetch_base_func,
void *fetch_base_baton,
- struct extra_baton *exb,
+ struct svn_delta__extra_baton *exb,
apr_pool_t *pool)
{
/* Static 'cause we don't want it to be on the stack. */
@@ -959,13 +967,20 @@ delta_from_editor(const svn_delta_editor
};
struct ev2_edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
+ if (!base_relpath)
+ base_relpath = "";
+ else if (base_relpath[0] == '/')
+ base_relpath += 1;
+
eb->editor = editor;
- eb->paths = apr_hash_make(pool);
+ eb->changes = apr_hash_make(pool);
eb->path_order = apr_array_make(pool, 1, sizeof(const char *));
eb->edit_pool = pool;
eb->found_abs_paths = found_abs_paths;
*eb->found_abs_paths = FALSE;
eb->exb = exb;
+ eb->repos_root = apr_pstrdup(pool, repos_root);
+ eb->base_relpath = apr_pstrdup(pool, base_relpath);
eb->fetch_props_func = fetch_props_func;
eb->fetch_props_baton = fetch_props_baton;
@@ -982,34 +997,13 @@ delta_from_editor(const svn_delta_editor
return SVN_NO_ERROR;
}
-
-
-
+/* ### note the similarity to struct change_node. these structures will
+ ### be combined in the future. */
struct operation {
- enum {
- OP_OPEN,
- OP_DELETE,
- OP_ADD,
- OP_REPLACE,
- OP_ADD_ABSENT,
- OP_PROPSET /* only for files for which no other operation is
- occuring; directories are OP_OPEN with non-empty
- props */
- } operation;
-
- const char *path;
- svn_kind_t kind; /* to copy, mkdir, put or set revprops */
- svn_revnum_t base_revision; /* When committing, the base revision */
- svn_revnum_t copyfrom_revision; /* to copy, valid for add and replace */
- svn_checksum_t *new_checksum; /* An MD5 hash of the new contents, if any */
- const char *copyfrom_url; /* to copy, valid for add and replace */
- const char *src_file; /* for put, the source file for contents */
- apr_hash_t *children; /* const char *path -> struct operation * */
- apr_hash_t *prop_mods; /* const char *prop_name ->
- const svn_string_t *prop_value */
- apr_array_header_t *prop_dels; /* const char *prop_name deletions */
- void *baton; /* as returned by the commit editor */
+ /* ### leave these two here for now. still used. */
+ svn_revnum_t base_revision;
+ void *baton;
};
struct editor_baton
@@ -1025,205 +1019,43 @@ struct editor_baton
struct operation root;
svn_boolean_t *make_abs_paths;
+ const char *repos_root;
+ const char *base_relpath;
+
+ /* REPOS_RELPATH -> struct change_node * */
+ apr_hash_t *changes;
- apr_hash_t *paths;
apr_pool_t *edit_pool;
};
-/* Find the operation associated with PATH, which is a single-path
- component representing a child of the path represented by
- OPERATION. If no such child operation exists, create a new one of
- type OP_OPEN. */
-static struct operation *
-get_operation(const char *path,
- struct operation *operation,
- svn_revnum_t base_revision,
- apr_pool_t *result_pool)
-{
- struct operation *child = apr_hash_get(operation->children, path,
- APR_HASH_KEY_STRING);
- if (! child)
- {
- child = apr_pcalloc(result_pool, sizeof(*child));
- child->children = apr_hash_make(result_pool);
- child->path = apr_pstrdup(result_pool, path);
- child->operation = OP_OPEN;
- child->copyfrom_revision = SVN_INVALID_REVNUM;
- child->kind = svn_kind_dir;
- child->base_revision = base_revision;
- child->prop_mods = apr_hash_make(result_pool);
- child->prop_dels = apr_array_make(result_pool, 1, sizeof(const char *));
- apr_hash_set(operation->children, apr_pstrdup(result_pool, path),
- APR_HASH_KEY_STRING, child);
- }
-
- /* If an operation has a child, it must of necessity be a directory,
- so ensure this fact. */
- operation->kind = svn_kind_dir;
-
- return child;
-}
-
-/* Add PATH to the operations tree rooted at OPERATION, creating any
- intermediate nodes that are required. Here's what's expected for
- each action type:
-
- ACTION URL REV SRC-FILE PROPNAME
- ------------ ----- ------- -------- --------
- ACTION_MKDIR NULL invalid NULL NULL
- ACTION_COPY valid valid NULL NULL
- ACTION_PUT NULL invalid valid NULL
- ACTION_DELETE NULL invalid NULL NULL
- ACTION_PROPSET valid invalid NULL valid
-
- Node type information is obtained for any copy source (to determine
- whether to create a file or directory) and for any deleted path (to
- ensure it exists since svn_delta_editor_t->delete_entry doesn't
- return an error on non-existent nodes). */
-static svn_error_t *
-build(struct editor_baton *eb,
- enum action_code_t action,
- const char *relpath,
- svn_kind_t kind,
- const char *url,
- svn_revnum_t rev,
- apr_hash_t *props,
- const char *src_file,
- svn_checksum_t *checksum,
- svn_revnum_t head,
- apr_pool_t *scratch_pool)
-{
- apr_array_header_t *path_bits = svn_path_decompose(relpath, scratch_pool);
- const char *path_so_far = "";
- struct operation *operation = &eb->root;
- int i;
-
- /* We should only see PROPS when action is ACTION_PROPSET. */
- SVN_ERR_ASSERT((props && action == ACTION_PROPSET)
- || (!props && action != ACTION_PROPSET) );
-
- /* Look for any previous operations we've recognized for PATH. If
- any of PATH's ancestors have not yet been traversed, we'll be
- creating OP_OPEN operations for them as we walk down PATH's path
- components. */
- for (i = 0; i < path_bits->nelts; ++i)
- {
- const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *);
- path_so_far = svn_relpath_join(path_so_far, path_bit, scratch_pool);
- operation = get_operation(path_so_far, operation, head, eb->edit_pool);
- }
-
- /* Handle property changes. */
- if (props)
- {
- apr_hash_t *current_props;
- apr_array_header_t *propdiffs;
-
- operation->kind = kind;
-
- if (operation->operation == OP_REPLACE)
- current_props = apr_hash_make(scratch_pool);
- else if (operation->copyfrom_url)
- SVN_ERR(eb->fetch_props_func(¤t_props, eb->fetch_props_baton,
- operation->copyfrom_url,
- operation->copyfrom_revision,
- scratch_pool, scratch_pool));
- else
- SVN_ERR(eb->fetch_props_func(¤t_props, eb->fetch_props_baton,
- relpath, rev, scratch_pool,
- scratch_pool));
-
- SVN_ERR(svn_prop_diffs(&propdiffs, props, current_props, scratch_pool));
-
- for (i = 0; i < propdiffs->nelts; i++)
- {
- /* Note: the array returned by svn_prop_diffs() is an array of
- actual structures, not pointers to them. */
- svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
- if (!prop->value)
- APR_ARRAY_PUSH(operation->prop_dels, const char *) =
- apr_pstrdup(eb->edit_pool, prop->name);
- else
- apr_hash_set(operation->prop_mods,
- apr_pstrdup(eb->edit_pool, prop->name),
- APR_HASH_KEY_STRING,
- svn_string_dup(prop->value, eb->edit_pool));
- }
-
- /* If we're not adding this thing ourselves, check for existence. */
- if (! ((operation->operation == OP_ADD) ||
- (operation->operation == OP_REPLACE)))
- {
- if ((operation->kind == svn_kind_file)
- && (operation->operation == OP_OPEN))
- operation->operation = OP_PROPSET;
- }
- if (!operation->copyfrom_revision)
- operation->copyfrom_revision = rev;
- return SVN_NO_ERROR;
- }
-
- if (action == ACTION_DELETE)
- {
- operation->operation = OP_DELETE;
- operation->base_revision = rev;
- }
-
- else if (action == ACTION_ADD_ABSENT)
- operation->operation = OP_ADD_ABSENT;
-
- /* Handle copy operations (which can be adds or replacements). */
- else if (action == ACTION_COPY)
- {
- operation->operation =
- operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
-
- if (kind == svn_kind_none || kind == svn_kind_unknown)
- SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
- url, rev, scratch_pool));
- else
- operation->kind = kind;
- operation->copyfrom_url = url;
- operation->copyfrom_revision = rev;
- }
- /* Handle mkdir operations (which can be adds or replacements). */
- else if (action == ACTION_MKDIR)
- {
- operation->operation =
- operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
- operation->kind = svn_kind_dir;
- }
- /* Handle put operations (which can be adds, replacements, or opens). */
- else if (action == ACTION_PUT)
- {
- if (operation->operation == OP_DELETE)
- {
- operation->operation = OP_REPLACE;
- }
- else if (operation->operation == OP_OPEN)
- {
- if (kind == svn_kind_file)
- operation->operation = OP_OPEN;
- else if (kind == svn_kind_none)
- operation->operation = OP_ADD;
- else
- return svn_error_createf(SVN_ERR_BAD_URL, NULL,
- "'%s' is not a file", relpath);
- }
- operation->kind = svn_kind_file;
- operation->src_file = apr_pstrdup(eb->edit_pool, src_file);
- operation->new_checksum = svn_checksum_dup(checksum, eb->edit_pool);
- }
- else
- {
- /* We shouldn't get here. */
- SVN_ERR_MALFUNCTION();
- }
+/* Insert a new change for RELPATH, or return an existing one. */
+static struct change_node *
+insert_change(const char *relpath,
+ apr_hash_t *changes)
+{
+ apr_pool_t *result_pool;
+ struct change_node *change;
+
+ change = apr_hash_get(changes, relpath, APR_HASH_KEY_STRING);
+ if (change != NULL)
+ return change;
+
+ result_pool = apr_hash_pool_get(changes);
+
+ /* Return an empty change. Callers will tweak as needed. */
+ change = apr_pcalloc(result_pool, sizeof(*change));
+ change->changing = SVN_INVALID_REVNUM;
+ change->deleting = SVN_INVALID_REVNUM;
+
+ apr_hash_set(changes,
+ apr_pstrdup(result_pool, relpath), APR_HASH_KEY_STRING,
+ change);
- return SVN_NO_ERROR;
+ return change;
}
+
/* This implements svn_editor_cb_add_directory_t */
static svn_error_t *
add_directory_cb(void *baton,
@@ -1234,24 +1066,12 @@ add_directory_cb(void *baton,
apr_pool_t *scratch_pool)
{
struct editor_baton *eb = baton;
+ struct change_node *change = insert_change(relpath, eb->changes);
- if (SVN_IS_VALID_REVNUM(replaces_rev))
- {
- /* We need to add the delete action. */
-
- SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
- NULL, SVN_INVALID_REVNUM,
- NULL, NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
- }
-
- SVN_ERR(build(eb, ACTION_MKDIR, relpath, svn_kind_dir,
- NULL, SVN_INVALID_REVNUM,
- NULL, NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
-
- if (props && apr_hash_count(props) > 0)
- SVN_ERR(build(eb, ACTION_PROPSET, relpath, svn_kind_dir,
- NULL, SVN_INVALID_REVNUM, props,
- NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+ change->action = RESTRUCTURE_ADD;
+ change->kind = svn_kind_dir;
+ change->deleting = replaces_rev;
+ change->props = svn_prop_hash_dup(props, eb->edit_pool);
return SVN_NO_ERROR;
}
@@ -1270,6 +1090,7 @@ add_file_cb(void *baton,
const char *tmp_filename;
svn_stream_t *tmp_stream;
svn_checksum_t *md5_checksum;
+ struct change_node *change = insert_change(relpath, eb->changes);
/* We may need to re-checksum these contents */
if (!(checksum && checksum->kind == svn_checksum_md5))
@@ -1278,30 +1099,18 @@ add_file_cb(void *baton,
else
md5_checksum = (svn_checksum_t *)checksum;
- if (SVN_IS_VALID_REVNUM(replaces_rev))
- {
- /* We need to add the delete action. */
-
- SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
- NULL, SVN_INVALID_REVNUM,
- NULL, NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
- }
-
/* Spool the contents to a tempfile, and provide that to the driver. */
SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_filename, NULL,
svn_io_file_del_on_pool_cleanup,
eb->edit_pool, scratch_pool));
SVN_ERR(svn_stream_copy3(contents, tmp_stream, NULL, NULL, scratch_pool));
- SVN_ERR(build(eb, ACTION_PUT, relpath, svn_kind_none,
- NULL, SVN_INVALID_REVNUM,
- NULL, tmp_filename, md5_checksum, SVN_INVALID_REVNUM,
- scratch_pool));
-
- if (props && apr_hash_count(props) > 0)
- SVN_ERR(build(eb, ACTION_PROPSET, relpath, svn_kind_file,
- NULL, SVN_INVALID_REVNUM, props,
- NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+ change->action = RESTRUCTURE_ADD;
+ change->kind = svn_kind_file;
+ change->deleting = replaces_rev;
+ change->props = svn_prop_hash_dup(props, eb->edit_pool);
+ change->contents_abspath = tmp_filename;
+ change->checksum = svn_checksum_dup(md5_checksum, eb->edit_pool);
return SVN_NO_ERROR;
}
@@ -1315,17 +1124,18 @@ add_symlink_cb(void *baton,
svn_revnum_t replaces_rev,
apr_pool_t *scratch_pool)
{
+#if 0
struct editor_baton *eb = baton;
+ struct change_node *change = insert_change(relpath, eb->changes);
- if (SVN_IS_VALID_REVNUM(replaces_rev))
- {
- /* We need to add the delete action. */
-
- SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
- NULL, SVN_INVALID_REVNUM,
- NULL, NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
- }
+ change->action = RESTRUCTURE_ADD;
+ change->kind = svn_kind_symlink;
+ change->deleting = replaces_rev;
+ change->props = svn_prop_hash_dup(props, eb->edit_pool);
+ /* ### target */
+#endif
+ SVN__NOT_IMPLEMENTED();
return SVN_NO_ERROR;
}
@@ -1338,10 +1148,11 @@ add_absent_cb(void *baton,
apr_pool_t *scratch_pool)
{
struct editor_baton *eb = baton;
+ struct change_node *change = insert_change(relpath, eb->changes);
- SVN_ERR(build(eb, ACTION_ADD_ABSENT, relpath, kind,
- NULL, SVN_INVALID_REVNUM,
- NULL, NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+ change->action = RESTRUCTURE_ADD_ABSENT;
+ change->kind = kind;
+ change->deleting = replaces_rev;
return SVN_NO_ERROR;
}
@@ -1351,16 +1162,22 @@ static svn_error_t *
alter_directory_cb(void *baton,
const char *relpath,
svn_revnum_t revision,
+ const apr_array_header_t *children,
apr_hash_t *props,
apr_pool_t *scratch_pool)
{
struct editor_baton *eb = baton;
+ struct change_node *change = insert_change(relpath, eb->changes);
/* ### should we verify the kind is truly a directory? */
- SVN_ERR(build(eb, ACTION_PROPSET, relpath, svn_kind_dir,
- NULL, SVN_INVALID_REVNUM,
- props, NULL, NULL, revision, scratch_pool));
+ /* ### do we need to do anything with CHILDREN? */
+
+ /* Note: this node may already have information in CHANGE as a result
+ of an earlier copy/move operation. */
+ change->kind = svn_kind_dir;
+ change->changing = revision;
+ change->props = svn_prop_hash_dup(props, eb->edit_pool);
return SVN_NO_ERROR;
}
@@ -1379,6 +1196,7 @@ alter_file_cb(void *baton,
const char *tmp_filename;
svn_stream_t *tmp_stream;
svn_checksum_t *md5_checksum;
+ struct change_node *change = insert_change(relpath, eb->changes);
/* ### should we verify the kind is truly a file? */
@@ -1398,17 +1216,19 @@ alter_file_cb(void *baton,
eb->edit_pool, scratch_pool));
SVN_ERR(svn_stream_copy3(contents, tmp_stream, NULL, NULL,
scratch_pool));
-
- SVN_ERR(build(eb, ACTION_PUT, relpath, svn_kind_file,
- NULL, SVN_INVALID_REVNUM,
- NULL, tmp_filename, md5_checksum, revision, scratch_pool));
}
- if (props)
+ /* Note: this node may already have information in CHANGE as a result
+ of an earlier copy/move operation. */
+
+ change->kind = svn_kind_file;
+ change->changing = revision;
+ if (props != NULL)
+ change->props = svn_prop_hash_dup(props, eb->edit_pool);
+ if (contents != NULL)
{
- SVN_ERR(build(eb, ACTION_PROPSET, relpath, svn_kind_file,
- NULL, SVN_INVALID_REVNUM,
- props, NULL, NULL, revision, scratch_pool));
+ change->contents_abspath = tmp_filename;
+ change->checksum = svn_checksum_dup(md5_checksum, eb->edit_pool);
}
return SVN_NO_ERROR;
@@ -1423,12 +1243,11 @@ alter_symlink_cb(void *baton,
const char *target,
apr_pool_t *scratch_pool)
{
- struct editor_baton *eb = baton;
-
/* ### should we verify the kind is truly a symlink? */
/* ### do something */
+ SVN__NOT_IMPLEMENTED();
return SVN_NO_ERROR;
}
@@ -1440,10 +1259,11 @@ delete_cb(void *baton,
apr_pool_t *scratch_pool)
{
struct editor_baton *eb = baton;
+ struct change_node *change = insert_change(relpath, eb->changes);
- SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
- NULL, revision, NULL, NULL, NULL, SVN_INVALID_REVNUM,
- scratch_pool));
+ change->action = RESTRUCTURE_DELETE;
+ /* change->kind = svn_kind_unknown; */
+ change->deleting = revision;
return SVN_NO_ERROR;
}
@@ -1458,19 +1278,22 @@ copy_cb(void *baton,
apr_pool_t *scratch_pool)
{
struct editor_baton *eb = baton;
+ struct change_node *change = insert_change(dst_relpath, eb->changes);
- if (SVN_IS_VALID_REVNUM(replaces_rev))
- {
- /* We need to add the delete action. */
-
- SVN_ERR(build(eb, ACTION_DELETE, dst_relpath, svn_kind_unknown,
- NULL, SVN_INVALID_REVNUM,
- NULL, NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
- }
+ change->action = RESTRUCTURE_ADD;
+ /* change->kind = svn_kind_unknown; */
+ change->deleting = replaces_rev;
+ change->copyfrom_path = apr_pstrdup(eb->edit_pool, src_relpath);
+ change->copyfrom_rev = src_revision;
+
+ /* We need the source's kind to know whether to call add_directory()
+ or add_file() later on. */
+ SVN_ERR(eb->fetch_kind_func(&change->kind, eb->fetch_kind_baton,
+ change->copyfrom_path,
+ change->copyfrom_rev,
+ scratch_pool));
- SVN_ERR(build(eb, ACTION_COPY, dst_relpath, svn_kind_unknown,
- src_relpath, src_revision, NULL, NULL, NULL,
- SVN_INVALID_REVNUM, scratch_pool));
+ /* Note: this node may later have alter_*() called on it. */
return SVN_NO_ERROR;
}
@@ -1485,6 +1308,30 @@ move_cb(void *baton,
apr_pool_t *scratch_pool)
{
struct editor_baton *eb = baton;
+ struct change_node *change;
+
+ /* Remap a move into a DELETE + COPY. */
+
+ change = insert_change(src_relpath, eb->changes);
+ change->action = RESTRUCTURE_DELETE;
+ /* change->kind = svn_kind_unknown; */
+ change->deleting = src_revision;
+
+ change = insert_change(dst_relpath, eb->changes);
+ change->action = RESTRUCTURE_ADD;
+ /* change->kind = svn_kind_unknown; */
+ change->deleting = replaces_rev;
+ change->copyfrom_path = apr_pstrdup(eb->edit_pool, src_relpath);
+ change->copyfrom_rev = src_revision;
+
+ /* We need the source's kind to know whether to call add_directory()
+ or add_file() later on. */
+ SVN_ERR(eb->fetch_kind_func(&change->kind, eb->fetch_kind_baton,
+ change->copyfrom_path,
+ change->copyfrom_rev,
+ scratch_pool));
+
+ /* Note: this node may later have alter_*() called on it. */
return SVN_NO_ERROR;
}
@@ -1496,205 +1343,418 @@ rotate_cb(void *baton,
const apr_array_header_t *revisions,
apr_pool_t *scratch_pool)
{
- struct editor_baton *eb = baton;
-
+ SVN__NOT_IMPLEMENTED();
return SVN_NO_ERROR;
}
-static svn_error_t *
-change_props(const svn_delta_editor_t *editor,
- void *baton,
- const struct operation *child,
- apr_pool_t *scratch_pool)
+
+static int
+count_components(const char *relpath)
{
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ int count = 1;
+ const char *slash = strchr(relpath, '/');
- if (child->prop_dels)
+ while (slash != NULL)
{
- int i;
- for (i = 0; i < child->prop_dels->nelts; i++)
+ ++count;
+ slash = strchr(slash + 1, '/');
+ }
+ return count;
+}
+
+
+static int
+sort_deletes_first(const svn_sort__item_t *item1,
+ const svn_sort__item_t *item2)
+{
+ const char *relpath1 = item1->key;
+ const char *relpath2 = item2->key;
+ const struct change_node *change1 = item1->value;
+ const struct change_node *change2 = item2->value;
+ const char *slash1;
+ const char *slash2;
+ ptrdiff_t len1;
+ ptrdiff_t len2;
+
+ /* Force the root to always sort first. Otherwise, it may look like a
+ sibling of its children (no slashes), and could get sorted *after*
+ any children that get deleted. */
+ if (*relpath1 == '\0')
+ return -1;
+ if (*relpath2 == '\0')
+ return 1;
+
+ /* Are these two items siblings? The 'if' statement tests if they are
+ siblings in the root directory, or that slashes were found in both
+ paths, that the length of the paths to those slashes match, and that
+ the path contents up to those slashes also match. */
+ slash1 = strrchr(relpath1, '/');
+ slash2 = strrchr(relpath2, '/');
+ if ((slash1 == NULL && slash2 == NULL)
+ || (slash1 != NULL
+ && slash2 != NULL
+ && (len1 = slash1 - relpath1) == (len2 = slash2 - relpath2)
+ && memcmp(relpath1, relpath2, len1) == 0))
+ {
+ if (change1->action == RESTRUCTURE_DELETE)
{
- const char *prop_name;
+ if (change2->action == RESTRUCTURE_DELETE)
+ {
+ /* If both items are being deleted, then we don't care about
+ the order. State they are equal. */
+ return 0;
+ }
- svn_pool_clear(iterpool);
- prop_name = APR_ARRAY_IDX(child->prop_dels, i, const char *);
- if (child->kind == svn_kind_dir)
- SVN_ERR(editor->change_dir_prop(baton, prop_name,
- NULL, iterpool));
- else
- SVN_ERR(editor->change_file_prop(baton, prop_name,
- NULL, iterpool));
+ /* ITEM1 is being deleted. Sort it before the surviving item. */
+ return -1;
}
+ if (change2->action == RESTRUCTURE_DELETE)
+ /* ITEM2 is being deleted. Sort it before the surviving item. */
+ return 1;
+
+ /* Normally, we don't care about the ordering of two siblings. However,
+ if these siblings are directories, then we need to provide an
+ ordering so that the quicksort algorithm will further sort them
+ relative to the maybe-directory's children.
+
+ Without this additional ordering, we could see that A/B/E and A/B/F
+ are equal. And then A/B/E/child is sorted before A/B/F. But since
+ E and F are "equal", A/B/E could arrive *after* A/B/F and after the
+ A/B/E/child node. */
+
+ /* FALLTHROUGH */
}
- if (apr_hash_count(child->prop_mods))
+ /* Paths-to-be-deleted with fewer components always sort earlier.
+
+ For example, gamma will sort before E/alpha.
+
+ Without this test, E/alpha lexicographically sorts before gamma,
+ but gamma sorts before E when gamma is to be deleted. This kind of
+ ordering would place E/alpha before E. Not good.
+
+ With this test, gamma sorts before E/alpha. E and E/alpha are then
+ sorted by svn_path_compare_paths() (which places E before E/alpha). */
+ if (change1->action == RESTRUCTURE_DELETE
+ || change2->action == RESTRUCTURE_DELETE)
{
- apr_hash_index_t *hi;
- for (hi = apr_hash_first(scratch_pool, child->prop_mods);
- hi; hi = apr_hash_next(hi))
- {
- const char *name = svn__apr_hash_index_key(hi);
- svn_string_t *val = svn__apr_hash_index_val(hi);
+ int count1 = count_components(relpath1);
+ int count2 = count_components(relpath2);
- svn_pool_clear(iterpool);
- if (child->kind == svn_kind_dir)
- SVN_ERR(editor->change_dir_prop(baton, name, val, iterpool));
- else
- SVN_ERR(editor->change_file_prop(baton, name, val, iterpool));
- }
+ if (count1 < count2 && change1->action == RESTRUCTURE_DELETE)
+ return -1;
+ if (count1 > count2 && change2->action == RESTRUCTURE_DELETE)
+ return 1;
+ }
+
+ /* Use svn_path_compare_paths() to get correct depth-based ordering. */
+ return svn_path_compare_paths(relpath1, relpath2);
+}
+
+
+static const apr_array_header_t *
+get_sorted_paths(apr_hash_t *changes,
+ const char *base_relpath,
+ apr_pool_t *scratch_pool)
+{
+ const apr_array_header_t *items;
+ apr_array_header_t *paths;
+ int i;
+
+ /* Construct a sorted array of svn_sort__item_t structs. Within a given
+ directory, nodes that are to be deleted will appear first. */
+ items = svn_sort__hash(changes, sort_deletes_first, scratch_pool);
+
+ /* Build a new array with just the paths, trimmed to relative paths for
+ the Ev1 drive. */
+ paths = apr_array_make(scratch_pool, items->nelts, sizeof(const char *));
+ for (i = items->nelts; i--; )
+ {
+ const svn_sort__item_t *item;
+
+ item = &APR_ARRAY_IDX(items, i, const svn_sort__item_t);
+ APR_ARRAY_IDX(paths, i, const char *)
+ = svn_relpath_skip_ancestor(base_relpath, item->key);
+ }
+
+ /* We didn't use PUSH, so set the proper number of elements. */
+ paths->nelts = items->nelts;
+
+ return paths;
+}
+
+
+static svn_error_t *
+drive_ev1_props(const struct editor_baton *eb,
+ const char *repos_relpath,
+ const struct change_node *change,
+ void *node_baton,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_hash_t *old_props;
+ apr_array_header_t *propdiffs;
+ const char *ev1_relpath;
+ int i;
+
+ /* If there are no properties to install, then just exit. */
+ if (change->props == NULL)
+ return SVN_NO_ERROR;
+
+ if (change->copyfrom_path)
+ {
+ /* The pristine properties are from the copy/move source. */
+ SVN_ERR(eb->fetch_props_func(&old_props, eb->fetch_props_baton,
+ change->copyfrom_path,
+ change->copyfrom_rev,
+ scratch_pool, iterpool));
+ }
+ else if (change->action == RESTRUCTURE_ADD)
+ {
+ /* Locally-added nodes have no pristine properties.
+
+ Note: we can use iterpool; this hash only needs to survive to
+ the propdiffs call, and there are no contents to preserve. */
+ old_props = apr_hash_make(iterpool);
+ }
+ else
+ {
+ /* Fetch the pristine properties for whatever we're editing. */
+ SVN_ERR(eb->fetch_props_func(&old_props, eb->fetch_props_baton,
+ repos_relpath, change->changing,
+ scratch_pool, iterpool));
+ }
+
+ SVN_ERR(svn_prop_diffs(&propdiffs, change->props, old_props, scratch_pool));
+
+ ev1_relpath = svn_relpath_skip_ancestor(eb->base_relpath, repos_relpath);
+
+ for (i = 0; i < propdiffs->nelts; i++)
+ {
+ /* Note: the array returned by svn_prop_diffs() is an array of
+ actual structures, not pointers to them. */
+ const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
+
+ svn_pool_clear(iterpool);
+
+ if (change->kind == svn_kind_dir)
+ SVN_ERR(eb->deditor->change_dir_prop(node_baton,
+ prop->name, prop->value,
+ iterpool));
+ else
+ SVN_ERR(eb->deditor->change_file_prop(node_baton,
+ prop->name, prop->value,
+ iterpool));
+ }
+
+ /* Handle the funky unlock protocol. Note: only possibly on files. */
+ if (change->unlock)
+ {
+ SVN_ERR_ASSERT(change->kind == svn_kind_file);
+ SVN_ERR(eb->deditor->change_file_prop(node_baton,
+ SVN_PROP_ENTRY_LOCK_TOKEN, NULL,
+ iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
+
+/* Conforms to svn_delta_path_driver_cb_func_t */
static svn_error_t *
-drive_tree(struct operation *op,
- const struct operation *parent_op,
- const svn_delta_editor_t *editor,
- svn_boolean_t make_abs_paths,
- apr_pool_t *scratch_pool)
+apply_change(void **dir_baton,
+ void *parent_baton,
+ void *callback_baton,
+ const char *ev1_relpath,
+ apr_pool_t *result_pool)
{
- const char *path = op->path;
+ /* ### fix this? */
+ apr_pool_t *scratch_pool = result_pool;
+ const struct editor_baton *eb = callback_baton;
+ const struct change_node *change;
+ const char *relpath;
+ void *file_baton = NULL;
+
+ /* Typically, we are not creating new directory batons. */
+ *dir_baton = NULL;
- if (path[0] != '/' && make_abs_paths)
- path = apr_pstrcat(scratch_pool, "/", path, NULL);
+ relpath = svn_relpath_join(eb->base_relpath, ev1_relpath, scratch_pool);
+ change = apr_hash_get(eb->changes, relpath, APR_HASH_KEY_STRING);
- /* Deletes and replacements are simple -- just delete the thing. */
- if (op->operation == OP_DELETE || op->operation == OP_REPLACE)
+ /* The callback should only be called for paths in CHANGES. */
+ SVN_ERR_ASSERT(change != NULL);
+
+ /* Are we editing the root of the tree? */
+ if (parent_baton == NULL)
{
- SVN_ERR(editor->delete_entry(path, op->base_revision,
- parent_op->baton, scratch_pool));
+ /* The root was opened in start_edit_func() */
+ *dir_baton = eb->root.baton;
+
+ /* Only property edits are allowed on the root. */
+ SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
+ SVN_ERR(drive_ev1_props(eb, relpath, change, *dir_baton, scratch_pool));
+
+ /* No further action possible for the root. */
+ return SVN_NO_ERROR;
}
- if (op->kind == svn_kind_dir)
+ if (change->action == RESTRUCTURE_DELETE)
{
- /* Open or create our baton. */
- if (op->operation == OP_OPEN || op->operation == OP_PROPSET)
- SVN_ERR(editor->open_directory(path, parent_op->baton,
- op->base_revision,
- scratch_pool, &op->baton));
+ SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting,
+ parent_baton, scratch_pool));
+
+ /* No futher action possible for this node. */
+ return SVN_NO_ERROR;
+ }
- else if (op->operation == OP_ADD || op->operation == OP_REPLACE)
- SVN_ERR(editor->add_directory(path, parent_op->baton,
- op->copyfrom_url, op->copyfrom_revision,
- scratch_pool, &op->baton));
+ /* If we're not deleting this node, then we should know its kind. */
+ SVN_ERR_ASSERT(change->kind != svn_kind_unknown);
- else if (op->operation == OP_ADD_ABSENT)
- SVN_ERR(editor->absent_directory(path, parent_op->baton,
+ if (change->action == RESTRUCTURE_ADD_ABSENT)
+ {
+ if (change->kind == svn_kind_dir)
+ SVN_ERR(eb->deditor->absent_directory(ev1_relpath, parent_baton,
+ scratch_pool));
+ else
+ SVN_ERR(eb->deditor->absent_file(ev1_relpath, parent_baton,
scratch_pool));
- if (op->baton)
- {
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- apr_hash_index_t *hi;
+ /* No further action possible for this node. */
+ return SVN_NO_ERROR;
+ }
+ /* RESTRUCTURE_NONE or RESTRUCTURE_ADD */
- /* Do any prop mods we may have. */
- SVN_ERR(change_props(editor, op->baton, op, scratch_pool));
+ if (change->action == RESTRUCTURE_ADD)
+ {
+ const char *copyfrom_url = NULL;
+ svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
- for (hi = apr_hash_first(scratch_pool, op->children);
- hi; hi = apr_hash_next(hi))
- {
- struct operation *child = svn__apr_hash_index_val(hi);
+ /* Do we have an old node to delete first? */
+ if (SVN_IS_VALID_REVNUM(change->deleting))
+ SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting,
+ parent_baton, scratch_pool));
- svn_pool_clear(iterpool);
- SVN_ERR(drive_tree(child, op, editor, make_abs_paths, iterpool));
- }
- svn_pool_destroy(iterpool);
+ /* Are we copying the node from somewhere? */
+ if (change->copyfrom_path)
+ {
+ if (eb->repos_root)
+ copyfrom_url = svn_path_url_add_component2(eb->repos_root,
+ change->copyfrom_path,
+ scratch_pool);
+ else
+ /* ### prefix with "/" ? */
+ copyfrom_url = change->copyfrom_path;
- /* We're done, close the directory. */
- SVN_ERR(editor->close_directory(op->baton, scratch_pool));
+ copyfrom_rev = change->copyfrom_rev;
}
+
+ if (change->kind == svn_kind_dir)
+ SVN_ERR(eb->deditor->add_directory(ev1_relpath, parent_baton,
+ copyfrom_url, copyfrom_rev,
+ result_pool, dir_baton));
+ else
+ SVN_ERR(eb->deditor->add_file(ev1_relpath, parent_baton,
+ copyfrom_url, copyfrom_rev,
+ result_pool, &file_baton));
}
else
{
- /* This currently treats anything that isn't a directory as a file.
- I don't know that that's a valid assumption... */
-
- void *file_baton = NULL;
-
- /* Open or create our baton. */
- if (op->operation == OP_OPEN || op->operation == OP_PROPSET)
- SVN_ERR(editor->open_file(path, parent_op->baton, op->base_revision,
- scratch_pool, &file_baton));
-
- else if (op->operation == OP_ADD || op->operation == OP_REPLACE)
- SVN_ERR(editor->add_file(path, parent_op->baton, op->copyfrom_url,
- op->copyfrom_revision, scratch_pool,
- &file_baton));
+ if (change->kind == svn_kind_dir)
+ SVN_ERR(eb->deditor->open_directory(ev1_relpath, parent_baton,
+ change->changing,
+ result_pool, dir_baton));
+ else
+ SVN_ERR(eb->deditor->open_file(ev1_relpath, parent_baton,
+ change->changing,
+ result_pool, &file_baton));
+ }
- else if (op->operation == OP_ADD_ABSENT)
- SVN_ERR(editor->absent_file(path, parent_op->baton, scratch_pool));
+ /* Apply any properties in CHANGE to the node. */
+ if (change->kind == svn_kind_dir)
+ SVN_ERR(drive_ev1_props(eb, relpath, change, *dir_baton, scratch_pool));
+ else
+ SVN_ERR(drive_ev1_props(eb, relpath, change, file_baton, scratch_pool));
- if (file_baton)
- {
- /* Do we need to change text contents? */
- if (op->src_file)
- {
- svn_txdelta_window_handler_t handler;
- void *handler_baton;
- svn_stream_t *contents;
-
- SVN_ERR(editor->apply_textdelta(file_baton, NULL, scratch_pool,
- &handler, &handler_baton));
- SVN_ERR(svn_stream_open_readonly(&contents, op->src_file,
- scratch_pool, scratch_pool));
- SVN_ERR(svn_txdelta_send_stream(contents, handler, handler_baton,
- NULL, scratch_pool));
- SVN_ERR(svn_stream_close(contents));
- }
+ if (change->contents_abspath)
+ {
+ svn_txdelta_window_handler_t handler;
+ void *handler_baton;
+ svn_stream_t *contents;
- /* Do any prop mods we may have. */
- SVN_ERR(change_props(editor, file_baton, op, scratch_pool));
+ /* ### would be nice to have a BASE_CHECKSUM, but hey: this is the
+ ### shim code... */
+ SVN_ERR(eb->deditor->apply_textdelta(file_baton, NULL, scratch_pool,
+ &handler, &handler_baton));
+ SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath,
+ scratch_pool, scratch_pool));
+ /* ### it would be nice to send a true txdelta here, but whatever. */
+ SVN_ERR(svn_txdelta_send_stream(contents, handler, handler_baton,
+ NULL, scratch_pool));
+ SVN_ERR(svn_stream_close(contents));
+ }
- /* Close the file. */
- SVN_ERR(editor->close_file(file_baton,
- svn_checksum_to_cstring(op->new_checksum,
- scratch_pool),
- scratch_pool));
- }
+ if (file_baton)
+ {
+ const char *digest = svn_checksum_to_cstring(change->checksum,
+ scratch_pool);
+ SVN_ERR(eb->deditor->close_file(file_baton, digest, scratch_pool));
}
return SVN_NO_ERROR;
}
-/* This is a special case of drive_tree(), meant to handle the root, which
- doesn't have a parent and should already be open. */
+
static svn_error_t *
-drive_root(struct operation *root,
- const svn_delta_editor_t *editor,
- svn_boolean_t make_abs_paths,
- apr_pool_t *scratch_pool)
+drive_changes(const struct editor_baton *eb,
+ apr_pool_t *scratch_pool)
{
- apr_hash_index_t *hi;
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ struct change_node *change;
+ const apr_array_header_t *paths;
- /* Early out: if we haven't opened the root yet (which would usually only
- be the case in an abort), there isn't much we can do here. */
- if (!root->baton)
+ /* If we never opened a root baton, then the caller aborted the editor
+ before it even began. There is nothing to do. Bail. */
+ if (eb->root.baton == NULL)
return SVN_NO_ERROR;
- /* Do any prop mods we may have. */
- SVN_ERR(change_props(editor, root->baton, root, scratch_pool));
-
- /* Now iterate over our children. */
- for (hi = apr_hash_first(scratch_pool, root->children);
- hi; hi = apr_hash_next(hi))
- {
- struct operation *child = svn__apr_hash_index_val(hi);
-
- svn_pool_clear(iterpool);
- SVN_ERR(drive_tree(child, root, editor, make_abs_paths, iterpool));
- }
-
- /* We need to close the root directory, but leave it to our caller to call
- close_ or abort_edit(). */
- SVN_ERR(editor->close_directory(root->baton, scratch_pool));
+ /* We need to make the path driver believe we want to make changes to
+ the root. Otherwise, it will attempt an open_root(), which we already
+ did in start_edit_func(). We can forge up a change record, if one
+ does not already exist. */
+ change = insert_change(eb->base_relpath, eb->changes);
+ change->kind = svn_kind_dir;
+ /* No property changes (tho they might exist from a real change). */
+
+ /* Get a sorted list of Ev1-relative paths. */
+ paths = get_sorted_paths(eb->changes, eb->base_relpath, scratch_pool);
+
+ /* We need to pass SVN_INVALID_REVNUM to the path_driver. It uses this
+ revision whenever it opens directory batons. If we specified a "real"
+ value, such as eb->root.base_revision, then it might use that for a
+ subdirectory *incorrectly*.
+
+ Specifically, log_tests 38 demonstrates that erroneous behavior when
+ it attempts to open "/A" at revision 0 (not there, of course).
+
+ All this said: passing SVN_INVALID_REVNUM to all of those
+ open_directory() calls is not the best behavior either, but it does
+ happen to magically work. (ugh)
+
+ Thankfully, we're moving away from this skitchy behavior to Ev2.
+
+ Final note: every other caller of svn_delta_path_driver() passes
+ SVN_INVALID_REVNUM, so we can't be the only goofball.
+
+ Note: dropping const on the callback_baton. */
+ SVN_ERR(svn_delta_path_driver2(eb->deditor, eb->dedit_baton, paths,
+ apply_change, (void *)eb,
+ scratch_pool));
return SVN_NO_ERROR;
}
+
/* This implements svn_editor_cb_complete_t */
static svn_error_t *
complete_cb(void *baton,
@@ -1704,7 +1764,7 @@ complete_cb(void *baton,
svn_error_t *err;
/* Drive the tree we've created. */
- err = drive_root(&eb->root, eb->deditor, *eb->make_abs_paths, scratch_pool);
+ err = drive_changes(eb, scratch_pool);
if (!err)
{
err = svn_error_compose_create(err, eb->deditor->close_edit(
@@ -1731,7 +1791,7 @@ abort_cb(void *baton,
point. */
/* Drive the tree we've created. */
- err = drive_root(&eb->root, eb->deditor, *eb->make_abs_paths, scratch_pool);
+ err = drive_changes(eb, scratch_pool);
err2 = eb->deditor->abort_edit(eb->dedit_baton, scratch_pool);
@@ -1753,6 +1813,10 @@ start_edit_func(void *baton,
struct editor_baton *eb = baton;
eb->root.base_revision = base_revision;
+
+ /* For some Ev1 editors (such as the repos commit editor), the root must
+ be open before can invoke any callbacks. The open_root() call sets up
+ stuff (eg. open an FS txn) which will be needed. */
SVN_ERR(eb->deditor->open_root(eb->dedit_baton, eb->root.base_revision,
eb->edit_pool, &eb->root.baton));
@@ -1778,25 +1842,14 @@ do_unlock(void *baton,
apr_pool_t *scratch_pool)
{
struct editor_baton *eb = baton;
- apr_array_header_t *path_bits = svn_path_decompose(path, scratch_pool);
- const char *path_so_far = "";
- struct operation *operation = &eb->root;
- int i;
-
- /* Look for any previous operations we've recognized for PATH. If
- any of PATH's ancestors have not yet been traversed, we'll be
- creating OP_OPEN operations for them as we walk down PATH's path
- components. */
- for (i = 0; i < path_bits->nelts; ++i)
- {
- const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *);
- path_so_far = svn_relpath_join(path_so_far, path_bit, scratch_pool);
- operation = get_operation(path_so_far, operation, SVN_INVALID_REVNUM,
- eb->edit_pool);
- }
- APR_ARRAY_PUSH(operation->prop_dels, const char *) =
- SVN_PROP_ENTRY_LOCK_TOKEN;
+ {
+ /* PATH is REPOS_RELPATH */
+ struct change_node *change = insert_change(path, eb->changes);
+
+ /* We will need to propagate a deletion of SVN_PROP_ENTRY_LOCK_TOKEN */
+ change->unlock = TRUE;
+ }
return SVN_NO_ERROR;
}
@@ -1827,14 +1880,16 @@ do_unlock(void *baton,
* will be used by the shim handlers if they need to determine the
* existing properties on a path.
*/
-static svn_error_t *
-editor_from_delta(svn_editor_t **editor_p,
- struct extra_baton **exb,
- unlock_func_t *unlock_func,
+svn_error_t *
+svn_delta__editor_from_delta(svn_editor_t **editor_p,
+ struct svn_delta__extra_baton **exb,
+ svn_delta__unlock_func_t *unlock_func,
void **unlock_baton,
const svn_delta_editor_t *deditor,
void *dedit_baton,
svn_boolean_t *send_abs_paths,
+ const char *repos_root,
+ const char *base_relpath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_delta_fetch_kind_func_t fetch_kind_func,
@@ -1861,26 +1916,28 @@ editor_from_delta(svn_editor_t **editor_
abort_cb
};
struct editor_baton *eb = apr_pcalloc(result_pool, sizeof(*eb));
- struct extra_baton *extra_baton = apr_pcalloc(result_pool,
+ struct svn_delta__extra_baton *extra_baton = apr_pcalloc(result_pool,
sizeof(*extra_baton));
+ if (!base_relpath)
+ base_relpath = "";
+ else if (base_relpath[0] == '/')
+ base_relpath += 1;
+
eb->deditor = deditor;
eb->dedit_baton = dedit_baton;
eb->edit_pool = result_pool;
- eb->paths = apr_hash_make(result_pool);
+ eb->repos_root = apr_pstrdup(result_pool, repos_root);
+ eb->base_relpath = apr_pstrdup(result_pool, base_relpath);
+
+ eb->changes = apr_hash_make(result_pool);
eb->fetch_kind_func = fetch_kind_func;
eb->fetch_kind_baton = fetch_kind_baton;
eb->fetch_props_func = fetch_props_func;
eb->fetch_props_baton = fetch_props_baton;
- eb->root.path = NULL;
- eb->root.children = apr_hash_make(result_pool);
- eb->root.kind = svn_kind_dir;
- eb->root.operation = OP_OPEN;
- eb->root.prop_mods = apr_hash_make(result_pool);
- eb->root.prop_dels = apr_array_make(result_pool, 1, sizeof(const char *));
- eb->root.copyfrom_revision = SVN_INVALID_REVNUM;
+ eb->root.base_revision = SVN_INVALID_REVNUM;
eb->make_abs_paths = send_abs_paths;
@@ -1910,20 +1967,22 @@ svn_delta_shim_callbacks_default(apr_poo
return shim_callbacks;
}
-/* Uncomment below to add editor shims throughout Subversion. In it's
- * current state, that will likely break The World. */
-/* #define ENABLE_EDITOR_SHIMS*/
+/* To enable editor shims throughout Subversion, ENABLE_EV2_SHIMS should be
+ * defined. This can be done manually, or by providing `--enable-ev2-shims'
+ * to `configure'. */
svn_error_t *
svn_editor__insert_shims(const svn_delta_editor_t **deditor_out,
void **dedit_baton_out,
const svn_delta_editor_t *deditor_in,
void *dedit_baton_in,
+ const char *repos_root,
+ const char *base_relpath,
svn_delta_shim_callbacks_t *shim_callbacks,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
-#ifndef ENABLE_EDITOR_SHIMS
+#ifndef ENABLE_EV2_SHIMS
/* Shims disabled, just copy the editor and baton directly. */
*deditor_out = deditor_in;
*dedit_baton_out = dedit_baton_in;
@@ -1935,9 +1994,9 @@ svn_editor__insert_shims(const svn_delta
/* The "extra baton" is a set of functions and a baton which allows the
shims to communicate additional events to each other.
- editor_from_delta() returns a pointer to this baton, which
- delta_from_editor() should then store. */
- struct extra_baton *exb;
+ svn_delta__editor_from_delta() returns a pointer to this baton, which
+ svn_delta__delta_from_editor() should then store. */
+ struct svn_delta__extra_baton *exb;
/* The reason this is a pointer is that we don't know the appropriate
value until we start receiving paths. So process_actions() sets the
@@ -1945,24 +2004,27 @@ svn_editor__insert_shims(const svn_delta
svn_boolean_t *found_abs_paths = apr_palloc(result_pool,
sizeof(*found_abs_paths));
- unlock_func_t unlock_func;
+ svn_delta__unlock_func_t unlock_func;
void *unlock_baton;
SVN_ERR_ASSERT(shim_callbacks->fetch_kind_func != NULL);
SVN_ERR_ASSERT(shim_callbacks->fetch_props_func != NULL);
SVN_ERR_ASSERT(shim_callbacks->fetch_base_func != NULL);
- SVN_ERR(editor_from_delta(&editor, &exb, &unlock_func, &unlock_baton,
+ SVN_ERR(svn_delta__editor_from_delta(&editor, &exb,
+ &unlock_func, &unlock_baton,
deditor_in, dedit_baton_in,
- found_abs_paths, NULL, NULL,
+ found_abs_paths, repos_root, base_relpath,
+ NULL, NULL,
shim_callbacks->fetch_kind_func,
shim_callbacks->fetch_baton,
shim_callbacks->fetch_props_func,
shim_callbacks->fetch_baton,
result_pool, scratch_pool));
- SVN_ERR(delta_from_editor(deditor_out, dedit_baton_out, editor,
+ SVN_ERR(svn_delta__delta_from_editor(deditor_out, dedit_baton_out, editor,
unlock_func, unlock_baton,
found_abs_paths,
+ repos_root, base_relpath,
shim_callbacks->fetch_props_func,
shim_callbacks->fetch_baton,
shim_callbacks->fetch_base_func,
Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_delta/debug_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_delta/debug_editor.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_delta/debug_editor.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_delta/debug_editor.c Wed May 16 20:32:43 2012
@@ -21,6 +21,8 @@
* ====================================================================
*/
+#include "svn_io.h"
+
#include "debug_editor.h"
struct edit_baton
@@ -51,7 +53,7 @@ write_indent(struct edit_baton *eb, apr_
int i;
for (i = 0; i < eb->indent_level; ++i)
- SVN_ERR(svn_stream_printf(eb->out, pool, " "));
+ SVN_ERR(svn_stream_puts(eb->out, " "));
return SVN_NO_ERROR;
}