You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pb...@apache.org on 2012/06/26 21:28:22 UTC
svn commit: r1354186 [9/33] - in /subversion/branches/inheritable-props: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/emacs/ contrib/server-side/ notes/
notes/api-errata/1.8/ notes/directory-in...
Modified: subversion/branches/inheritable-props/subversion/libsvn_delta/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_delta/compat.c?rev=1354186&r1=1354185&r2=1354186&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_delta/compat.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_delta/compat.c Tue Jun 26 19:26:49 2012
@@ -21,6 +21,8 @@
* ====================================================================
*/
+#include <stddef.h>
+
#include "svn_types.h"
#include "svn_error.h"
#include "svn_delta.h"
@@ -112,9 +114,8 @@ struct ev2_edit_baton
{
svn_editor_t *editor;
- /* ### need to ensure we understand the proper root for these relpaths */
- apr_hash_t *changes; /* RELPATH -> struct change_node */
- apr_hash_t *paths;
+ apr_hash_t *changes; /* REPOS_RELPATH -> struct change_node */
+
apr_array_header_t *path_order;
int paths_processed;
@@ -157,31 +158,6 @@ struct ev2_file_baton
const char *delta_base;
};
-enum action_code_t
-{
- ACTION_MOVE,
- ACTION_MKDIR,
- ACTION_COPY,
- ACTION_PROPSET,
- ACTION_PUT,
- ACTION_ADD,
- ACTION_DELETE,
- ACTION_ADD_ABSENT,
- ACTION_UNLOCK
-};
-
-struct path_action
-{
- enum action_code_t action;
- void *args;
-};
-
-struct copy_args
-{
- const char *copyfrom_path;
- svn_revnum_t copyfrom_rev;
-};
-
enum restructure_action_t
{
RESTRUCTURE_NONE = 0,
@@ -198,8 +174,17 @@ struct change_node
svn_kind_t kind; /* the NEW kind of this node */
- /* The revision we're trying to change. Replace it, modify it, etc. */
- svn_revnum_t base_revision;
+ /* 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 */
@@ -210,37 +195,10 @@ struct change_node
RESTRUCTURE must be RESTRUCTURE_ADD. */
const char *copyfrom_path;
svn_revnum_t copyfrom_rev;
-};
-
-
-static svn_error_t *
-add_action(struct ev2_edit_baton *eb,
- const char *path,
- enum action_code_t action,
- void *args)
-{
- struct path_action *p_action;
- apr_array_header_t *action_list = apr_hash_get(eb->paths, path,
- APR_HASH_KEY_STRING);
-
- p_action = apr_palloc(eb->edit_pool, sizeof(*p_action));
- p_action->action = action;
- p_action->args = args;
-
- 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;
- }
-
- APR_ARRAY_PUSH(action_list, struct path_action *) = p_action;
-
- return SVN_NO_ERROR;
-}
+ /* Record whether an incoming propchange unlocked this node. */
+ svn_boolean_t unlock;
+};
static struct change_node *
@@ -249,29 +207,18 @@ locate_change(struct ev2_edit_baton *eb,
{
struct change_node *change = apr_hash_get(eb->changes, relpath,
APR_HASH_KEY_STRING);
- apr_array_header_t *action_list;
if (change != NULL)
return change;
- /* Shift RELPATH into the proper pool. */
+ /* Shift RELPATH into the proper pool, and record the observed order. */
relpath = apr_pstrdup(eb->edit_pool, relpath);
-
- /* Investigate whether there is an action in PATHS. Any presence there
- will determine whether we need to update PATH_ORDER. */
- action_list = apr_hash_get(eb->paths, relpath, APR_HASH_KEY_STRING);
- if (action_list == NULL)
- {
- /* Store an empty ACTION_LIST into PATHS. */
- action_list = apr_array_make(eb->edit_pool, 1,
- sizeof(struct path_action *));
- apr_hash_set(eb->paths, relpath, APR_HASH_KEY_STRING, action_list);
- APR_ARRAY_PUSH(eb->path_order, const char *) = 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->base_revision = SVN_INVALID_REVNUM;
+ change->changing = SVN_INVALID_REVNUM;
+ change->deleting = SVN_INVALID_REVNUM;
apr_hash_set(eb->changes, relpath, APR_HASH_KEY_STRING, change);
@@ -293,9 +240,10 @@ apply_propedit(struct ev2_edit_baton *eb
SVN_ERR_ASSERT(change->kind == svn_kind_unknown || change->kind == kind);
change->kind = kind;
- SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->base_revision)
- || change->base_revision == base_revision);
- change->base_revision = base_revision;
+ /* 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)
{
@@ -342,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;
@@ -370,179 +314,133 @@ get_children(struct ev2_edit_baton *eb,
static svn_error_t *
process_actions(struct ev2_edit_baton *eb,
- const char *path,
- apr_array_header_t *actions,
+ const char *repos_relpath,
const struct change_node *change,
apr_pool_t *scratch_pool)
{
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 == '/')
- {
- path++;
- *eb->found_abs_paths = TRUE;
- }
-
- /* Go through all of our actions, populating various datastructures
- * dependent on them. */
- for (i = 0; i < actions->nelts; i++)
- {
- const struct path_action *action = APR_ARRAY_IDX(actions, i,
- struct path_action *);
-
- switch (action->action)
- {
- case ACTION_DELETE:
- {
- delete_revnum = *((svn_revnum_t *) action->args);
- need_delete = TRUE;
- break;
- }
- case ACTION_ADD:
- {
- kind = *((svn_kind_t *) action->args);
- need_add = TRUE;
-
- if (kind == svn_kind_dir)
- {
- 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_COPY:
- {
- struct copy_args *c_args = action->args;
-
- copyfrom_path = c_args->copyfrom_path;
- copyfrom_rev = c_args->copyfrom_rev;
- need_copy = TRUE;
- break;
- }
+ SVN_ERR_ASSERT(change != NULL);
- case ACTION_ADD_ABSENT:
- {
- kind = *((svn_kind_t *) action->args);
- SVN_ERR(svn_editor_add_absent(eb->editor, path, kind,
- SVN_INVALID_REVNUM));
- break;
- }
+ if (change->unlock)
+ SVN_ERR(eb->do_unlock(eb->unlock_baton, repos_relpath, scratch_pool));
- case ACTION_UNLOCK:
- {
- SVN_ERR(eb->do_unlock(eb->unlock_baton, path, scratch_pool));
- break;
- }
+ if (change->action == RESTRUCTURE_DELETE)
+ {
+ /* 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));
- default:
- SVN_ERR_MALFUNCTION();
- }
+ /* No further work possible on this node. */
+ return SVN_NO_ERROR;
}
-
- if (change != NULL)
+ if (change->action == RESTRUCTURE_ADD_ABSENT)
{
- if (change->contents_abspath != NULL)
- {
- /* We can only set text on files. */
- /* ### validate we aren't overwriting KIND? */
- kind = svn_kind_file;
-
- /* ### 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));
-
- text_base_revision = change->base_revision;
- }
+ SVN_ERR(svn_editor_add_absent(eb->editor, repos_relpath,
+ change->kind, change->deleting));
- if (change->props != NULL)
- {
- /* ### validate we aren't overwriting KIND? */
- kind = change->kind;
- props = change->props;
- props_base_revision = change->base_revision;
- }
+ /* 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 (change->contents_abspath != NULL)
+ {
+ /* We can only set text on files. */
+ /* ### validate we aren't overwriting KIND? */
+ kind = svn_kind_file;
+
+ /* ### 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));
+ }
- if (need_delete && !need_add && !need_copy)
+ if (change->props != NULL)
{
- /* If we're only doing a delete, do it here. */
- SVN_ERR(svn_editor_delete(eb->editor, path, delete_revnum));
- return SVN_NO_ERROR;
+ /* ### validate we aren't overwriting KIND? */
+ kind = change->kind;
+ props = change->props;
}
- if (need_add)
+ if (change->action == RESTRUCTURE_ADD)
{
- if (props == NULL)
- props = apr_hash_make(scratch_pool);
+ /* An add might be a replace. Grab the revnum we're replacing. */
+ svn_revnum_t replaces_rev = change->deleting;
- if (kind == svn_kind_dir)
+ kind = change->kind;
+
+ if (change->copyfrom_path != NULL)
{
- SVN_ERR(svn_editor_add_directory(eb->editor, path, children,
- props, delete_revnum));
+ 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
{
- SVN_ERR(svn_editor_add_file(eb->editor, path, checksum, contents,
- props, delete_revnum));
- }
+ /* If no properties were defined, then use an empty set. */
+ if (props == NULL)
+ props = apr_hash_make(scratch_pool);
- return SVN_NO_ERROR;
- }
+ if (kind == svn_kind_dir)
+ {
+ const apr_array_header_t *children;
- if (need_copy)
- {
- SVN_ERR(svn_editor_copy(eb->editor, copyfrom_path, copyfrom_rev, path,
- delete_revnum));
+ children = get_children(eb, repos_relpath, scratch_pool);
+ SVN_ERR(svn_editor_add_directory(eb->editor, repos_relpath,
+ children, props,
+ replaces_rev));
+ }
+ else
+ {
+ /* 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)
+ {
+ contents = svn_stream_empty(scratch_pool);
+ checksum = svn_checksum_empty_checksum(svn_checksum_sha1,
+ scratch_pool);
+ }
+
+ SVN_ERR(svn_editor_add_file(eb->editor, repos_relpath,
+ checksum, contents, props,
+ replaces_rev));
+ }
+
+ /* No further work possible on this node. */
+ return SVN_NO_ERROR;
+ }
}
+#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)
{
- /* 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;
+ /* Changes to properties or content should have indicated the revision
+ it was intending to change.
+
+ Oop. Not true. The node may be locally-added. */
+#if 0
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(change->changing));
+#endif
+
+ /* ### 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));
}
@@ -561,16 +459,16 @@ run_ev2_actions(struct ev2_edit_baton *e
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);
- struct change_node *change = apr_hash_get(eb->changes, 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(eb, path, actions, change, iterpool));
+ SVN_ERR(process_actions(eb, repos_relpath, change, iterpool));
}
svn_pool_destroy(iterpool);
@@ -639,30 +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))
- *revnum = revision;
+ base_revision = revision;
else
- *revnum = pb->base_revision;
+ base_revision = pb->base_revision;
- SVN_ERR(add_action(pb->eb, relpath, ACTION_DELETE, revnum));
-
- /* ### note: cannot switch to CHANGES just yet. the action loop needs
- ### to see a delete action, and set NEED_DELETE. that is used for
- ### the file properties. once fileprops are converted, then we
- ### can fully switch over. */
-
- /* ### assert that RESTRUCTURE is NONE? */
+ SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
change->action = RESTRUCTURE_DELETE;
-#if 0
- SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->base_revision)
- || change->base_revision == revision);
- change->base_revision = revision;
-#endif
+ SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->deleting)
+ || change->deleting == base_revision);
+ change->deleting = base_revision;
return SVN_NO_ERROR;
}
@@ -684,6 +573,7 @@ ev2_add_directory(const char *path,
/* ### 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, relpath);
@@ -692,12 +582,6 @@ ev2_add_directory(const char *path,
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, relpath, ACTION_ADD, kind));
-
if (pb->copyfrom_relpath)
{
const char *name = svn_relpath_basename(relpath, scratch_pool);
@@ -709,16 +593,11 @@ ev2_add_directory(const char *path,
else
{
/* A copy */
- struct copy_args *args = apr_palloc(pb->eb->edit_pool, sizeof(*args));
change->copyfrom_path = map_to_repos_relpath(pb->eb, copyfrom_path,
pb->eb->edit_pool);
change->copyfrom_rev = copyfrom_revision;
- args->copyfrom_path = change->copyfrom_path;
- args->copyfrom_rev = change->copyfrom_rev;
- SVN_ERR(add_action(pb->eb, relpath, ACTION_COPY, args));
-
cb->copyfrom_relpath = change->copyfrom_path;
cb->copyfrom_rev = change->copyfrom_rev;
}
@@ -784,11 +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, relpath, 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;
}
@@ -810,6 +690,7 @@ ev2_add_file(const char *path,
/* ### 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, relpath);
@@ -818,19 +699,12 @@ ev2_add_file(const char *path,
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, relpath, 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);
@@ -841,10 +715,6 @@ ev2_add_file(const char *path,
change->copyfrom_path,
change->copyfrom_rev,
result_pool, scratch_pool));
-
- args->copyfrom_path = change->copyfrom_path;
- args->copyfrom_rev = change->copyfrom_rev;
- SVN_ERR(add_action(pb->eb, relpath, ACTION_COPY, args));
}
return SVN_NO_ERROR;
@@ -933,22 +803,24 @@ ev2_apply_textdelta(void *file_baton,
struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
struct change_node *change;
svn_stream_t *target;
+ /* ### fix this. for now, we know this has a "short" lifetime. */
+ apr_pool_t *scratch_pool = handler_pool;
change = locate_change(fb->eb, fb->path);
SVN_ERR_ASSERT(change->contents_abspath == NULL);
- SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->base_revision)
- || change->base_revision == fb->base_revision);
- change->base_revision = fb->base_revision;
+ 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, &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,
@@ -975,7 +847,13 @@ ev2_change_file_prop(void *file_baton,
{
/* 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));
+
+ /* ### 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(apply_propedit(fb->eb, fb->path, svn_kind_file, fb->base_revision,
@@ -998,11 +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, relpath, 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;
}
@@ -1095,7 +974,6 @@ svn_delta__delta_from_editor(const svn_d
eb->editor = editor;
eb->changes = apr_hash_make(pool);
- eb->paths = 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;
@@ -1123,29 +1001,9 @@ svn_delta__delta_from_editor(const svn_d
/* ### 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
@@ -1164,228 +1022,40 @@ struct editor_baton
const char *repos_root;
const char *base_relpath;
- apr_hash_t *paths;
+ /* REPOS_RELPATH -> struct change_node * */
+ apr_hash_t *changes;
+
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)
+/* Insert a new change for RELPATH, or return an existing one. */
+static struct change_node *
+insert_change(const char *relpath,
+ apr_hash_t *changes)
{
- 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;
- 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) );
-
- relpath = svn_relpath_skip_ancestor(eb->base_relpath, relpath);
-
- /* 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. */
- path_bits = svn_path_decompose(relpath, scratch_pool);
- 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)
- {
- const char *copyfrom_relpath;
-
- if (eb->repos_root)
- copyfrom_relpath = svn_uri_skip_ancestor(eb->repos_root,
- operation->copyfrom_url,
- scratch_pool);
- else
- copyfrom_relpath = operation->copyfrom_url;
-
- SVN_ERR(eb->fetch_props_func(¤t_props, eb->fetch_props_baton,
- copyfrom_relpath,
- operation->copyfrom_revision,
- scratch_pool, scratch_pool));
- }
- else
- SVN_ERR(eb->fetch_props_func(¤t_props, eb->fetch_props_baton,
- svn_relpath_join(eb->base_relpath,
- relpath,
- scratch_pool),
- 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 (!SVN_IS_VALID_REVNUM(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;
+ apr_pool_t *result_pool;
+ struct change_node *change;
- 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;
+ change = apr_hash_get(changes, relpath, APR_HASH_KEY_STRING);
+ if (change != NULL)
+ return change;
- operation->copyfrom_revision = rev;
+ result_pool = apr_hash_pool_get(changes);
- if (eb->repos_root)
- operation->copyfrom_url = svn_path_url_add_component2(eb->repos_root,
- url,
- eb->edit_pool);
- else
- operation->copyfrom_url = apr_pstrcat(eb->edit_pool, "/", url, NULL);
- }
- /* 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();
- }
+ /* 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,
@@ -1396,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;
}
@@ -1432,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))
@@ -1440,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;
}
@@ -1477,18 +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
- return SVN_NO_ERROR;
+ SVN__NOT_IMPLEMENTED();
}
/* This implements svn_editor_cb_add_absent_t */
@@ -1500,10 +1147,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;
}
@@ -1513,16 +1161,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;
}
@@ -1541,6 +1195,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? */
@@ -1560,17 +1215,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;
@@ -1590,7 +1247,6 @@ alter_symlink_cb(void *baton,
/* ### do something */
SVN__NOT_IMPLEMENTED();
- return SVN_NO_ERROR;
}
/* This implements svn_editor_cb_delete_t */
@@ -1601,10 +1257,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;
}
@@ -1619,19 +1276,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;
}
@@ -1646,23 +1306,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. */
- /* Delete the move source. */
- SVN_ERR(build(eb, ACTION_DELETE, src_relpath, svn_kind_unknown,
- NULL, src_revision, NULL, NULL, NULL, SVN_INVALID_REVNUM,
- scratch_pool));
-
- if (SVN_IS_VALID_REVNUM(replaces_rev))
- {
- /* We need to add the delete action for the replaced target. */
- SVN_ERR(build(eb, ACTION_DELETE, dst_relpath, svn_kind_unknown,
- NULL, SVN_INVALID_REVNUM,
- NULL, NULL, NULL, SVN_INVALID_REVNUM, 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));
+ 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;
}
@@ -1675,203 +1342,413 @@ rotate_cb(void *baton,
apr_pool_t *scratch_pool)
{
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, '/');
+
+ while (slash != NULL)
+ {
+ ++count;
+ slash = strchr(slash + 1, '/');
+ }
+ return count;
+}
- if (child->prop_dels)
+
+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))
{
- int i;
- for (i = 0; i < child->prop_dels->nelts; i++)
+ 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;
}
- svn_pool_destroy(iterpool);
- return SVN_NO_ERROR;
+ /* 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_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)
+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)
{
- const char *path = op->path;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_hash_t *old_props;
+ apr_array_header_t *propdiffs;
+ int i;
- if (path[0] != '/' && make_abs_paths)
- path = apr_pstrcat(scratch_pool, "/", path, NULL);
+ /* If there are no properties to install, then just exit. */
+ if (change->props == NULL)
+ return SVN_NO_ERROR;
- /* Deletes and replacements are simple -- just delete the thing. */
- if (op->operation == OP_DELETE || op->operation == OP_REPLACE)
+ if (change->copyfrom_path)
{
- SVN_ERR(editor->delete_entry(path, op->base_revision,
- parent_op->baton, scratch_pool));
+ /* 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.
- if (op->kind == svn_kind_dir)
+ 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
{
- /* 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));
+ /* 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));
+ }
- 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));
+ SVN_ERR(svn_prop_diffs(&propdiffs, change->props, old_props, scratch_pool));
- else if (op->operation == OP_ADD_ABSENT)
- SVN_ERR(editor->absent_directory(path, parent_op->baton,
- 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. */
+ const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
- if (op->baton)
- {
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- apr_hash_index_t *hi;
+ svn_pool_clear(iterpool);
- /* Do any prop mods we may have. */
- SVN_ERR(change_props(editor, op->baton, op, scratch_pool));
+ 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));
+ }
- for (hi = apr_hash_first(scratch_pool, op->children);
- hi; hi = apr_hash_next(hi))
- {
- struct operation *child = svn__apr_hash_index_val(hi);
+ /* 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_clear(iterpool);
- SVN_ERR(drive_tree(child, op, editor, make_abs_paths, iterpool));
- }
- svn_pool_destroy(iterpool);
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
- /* We're done, close the directory. */
- SVN_ERR(editor->close_directory(op->baton, scratch_pool));
- }
+
+/* Conforms to svn_delta_path_driver_cb_func_t */
+static svn_error_t *
+apply_change(void **dir_baton,
+ void *parent_baton,
+ void *callback_baton,
+ const char *ev1_relpath,
+ apr_pool_t *result_pool)
+{
+ /* ### 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;
+
+ relpath = svn_relpath_join(eb->base_relpath, ev1_relpath, scratch_pool);
+ change = apr_hash_get(eb->changes, relpath, APR_HASH_KEY_STRING);
+
+ /* 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)
+ {
+ /* 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;
}
- else
+
+ if (change->action == RESTRUCTURE_DELETE)
{
- /* This currently treats anything that isn't a directory as a file.
- I don't know that that's a valid assumption... */
+ SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting,
+ parent_baton, scratch_pool));
- void *file_baton = NULL;
+ /* No futher action possible for this node. */
+ return SVN_NO_ERROR;
+ }
- /* 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));
+ /* 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 || 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->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));
- else if (op->operation == OP_ADD_ABSENT)
- SVN_ERR(editor->absent_file(path, parent_op->baton, scratch_pool));
+ /* No further action possible for this node. */
+ return SVN_NO_ERROR;
+ }
+ /* RESTRUCTURE_NONE or RESTRUCTURE_ADD */
- 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->action == RESTRUCTURE_ADD)
+ {
+ const char *copyfrom_url = NULL;
+ svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
- /* Do any prop mods we may have. */
- SVN_ERR(change_props(editor, file_baton, op, scratch_pool));
+ /* 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));
- /* Close the file. */
- SVN_ERR(editor->close_file(file_baton,
- svn_checksum_to_cstring(op->new_checksum,
- scratch_pool),
- scratch_pool));
+ /* 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;
+
+ 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
+ {
+ 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));
+ }
+
+ /* 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 (change->contents_abspath)
+ {
+ svn_txdelta_window_handler_t handler;
+ void *handler_baton;
+ svn_stream_t *contents;
+
+ /* ### 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));
+ }
+
+ 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,
@@ -1881,7 +1758,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(
@@ -1908,7 +1785,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);
@@ -1930,6 +1807,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));
@@ -1955,25 +1836,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;
}
@@ -2051,22 +1921,17 @@ svn_delta__editor_from_delta(svn_editor_
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;
Modified: subversion/branches/inheritable-props/subversion/libsvn_delta/debug_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_delta/debug_editor.c?rev=1354186&r1=1354185&r2=1354186&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_delta/debug_editor.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_delta/debug_editor.c Tue Jun 26 19:26:49 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;
}
Modified: subversion/branches/inheritable-props/subversion/libsvn_delta/editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_delta/editor.c?rev=1354186&r1=1354185&r2=1354186&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_delta/editor.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_delta/editor.c Tue Jun 26 19:26:49 2012
@@ -61,7 +61,7 @@ struct svn_editor_t
apr_hash_t *completed_nodes;
svn_boolean_t finished;
- apr_pool_t *result_pool;
+ apr_pool_t *state_pool;
#endif
};
@@ -105,7 +105,7 @@ static const int marker_added_dir;
#define MARK_RELPATH(editor, relpath, value) \
apr_hash_set((editor)->completed_nodes, \
- apr_pstrdup((editor)->result_pool, relpath), \
+ apr_pstrdup((editor)->state_pool, relpath), \
APR_HASH_KEY_STRING, value)
#define MARK_COMPLETED(editor, relpath) \
@@ -129,6 +129,36 @@ static const int marker_added_dir;
#define CHECK_UNKNOWN_CHILD(editor, relpath) \
SVN_ERR_ASSERT(check_unknown_child(editor, relpath))
+/* When a child is changed in some way, mark the parent directory as needing
+ to be "stable" (no future structural changes). IOW, only allow "alter" on
+ the parent. Prevents parent-add/delete/move after any child operation. */
+#define MARK_PARENT_STABLE(editor, relpath) \
+ mark_parent_stable(editor, relpath)
+
+/* If the parent is MARKER_ALLOW_ADD, then it has been moved-away, and we
+ know it does not exist. All other cases: it might exist. */
+#define VERIFY_PARENT_MAY_EXIST(editor, relpath) \
+ SVN_ERR_ASSERT(apr_hash_get((editor)->completed_nodes, \
+ svn_relpath_dirname(relpath, \
+ (editor)->scratch_pool), \
+ APR_HASH_KEY_STRING) != MARKER_ALLOW_ADD)
+
+/* If the parent is MARKER_ADDED_DIR, then we should not be deleting
+ children(*). If the parent is MARKER_ALLOW_ADD, then it has been
+ moved-away, so children cannot exist. That leaves MARKER_DONE,
+ MARKER_ALLOW_ALTER, and NULL as possible values. Just assert that
+ we didn't get either of the bad ones.
+
+ (*) if the child as added via add_*(), then it would have been marked
+ as completed and delete/move-away already test against completed nodes.
+ This test is to beware of trying to delete "children" that are not
+ actually (and can't possibly be) present. */
+#define CHILD_DELETIONS_ALLOWED(editor, relpath) \
+ SVN_ERR_ASSERT(!allow_either(editor, \
+ svn_relpath_dirname(relpath, \
+ (editor)->scratch_pool), \
+ MARKER_ADDED_DIR, MARKER_ALLOW_ADD))
+
static svn_boolean_t
allow_either(const svn_editor_t *editor,
const char *relpath,
@@ -166,6 +196,30 @@ check_unknown_child(const svn_editor_t *
return TRUE;
}
+static void
+mark_parent_stable(const svn_editor_t *editor,
+ const char *relpath)
+{
+ const char *parent = svn_relpath_dirname(relpath, editor->scratch_pool);
+ const void *marker = apr_hash_get(editor->completed_nodes,
+ parent, APR_HASH_KEY_STRING);
+
+ /* If RELPATH has already been marked (to disallow adds, or that it
+ has been fully-completed), then do nothing. */
+ if (marker == MARKER_ALLOW_ALTER
+ || marker == MARKER_DONE
+ || marker == MARKER_ADDED_DIR)
+ return;
+
+ /* If the marker is MARKER_ALLOW_ADD, then that means the parent was
+ moved away. There is no way to work on a child. That should have
+ been tested before we got here by VERIFY_PARENT_MAY_EXIST(). */
+ SVN_ERR_ASSERT_NO_RETURN(marker != MARKER_ALLOW_ADD);
+
+ /* MARKER is NULL. Upgrade it to MARKER_ALLOW_ALTER. */
+ MARK_RELPATH(editor, parent, MARKER_ALLOW_ALTER);
+}
+
#else
/* Be wary with the definition of these macros so that we don't
@@ -192,6 +246,10 @@ check_unknown_child(const svn_editor_t *
#define MARK_ADDED_DIR(editor, relpath) /* empty */
#define CHECK_UNKNOWN_CHILD(editor, relpath) /* empty */
+#define MARK_PARENT_STABLE(editor, relpath) /* empty */
+#define VERIFY_PARENT_MAY_EXIST(editor, relpath) /* empty */
+#define CHILD_DELETIONS_ALLOWED(editor, relpath) /* empty */
+
#endif /* ENABLE_ORDERING_CHECK */
@@ -214,7 +272,7 @@ svn_editor_create(svn_editor_t **editor,
(*editor)->pending_incomplete_children = apr_hash_make(result_pool);
(*editor)->completed_nodes = apr_hash_make(result_pool);
(*editor)->finished = FALSE;
- (*editor)->result_pool = result_pool;
+ (*editor)->state_pool = result_pool;
#endif
return SVN_NO_ERROR;
@@ -397,7 +455,7 @@ check_cancel(svn_editor_t *editor)
END_CALLBACK(editor);
}
- return err;
+ return svn_error_trace(err);
}
@@ -416,6 +474,7 @@ svn_editor_add_directory(svn_editor_t *e
/* ### validate children are just basenames? */
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, relpath);
CHECK_UNKNOWN_CHILD(editor, relpath);
SVN_ERR(check_cancel(editor));
@@ -430,6 +489,7 @@ svn_editor_add_directory(svn_editor_t *e
}
MARK_ADDED_DIR(editor, relpath);
+ MARK_PARENT_STABLE(editor, relpath);
CLEAR_INCOMPLETE(editor, relpath);
#ifdef ENABLE_ORDERING_CHECK
@@ -439,7 +499,7 @@ svn_editor_add_directory(svn_editor_t *e
{
const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
const char *child = svn_relpath_join(relpath, child_basename,
- editor->result_pool);
+ editor->state_pool);
apr_hash_set(editor->pending_incomplete_children, child,
APR_HASH_KEY_STRING, "");
@@ -448,7 +508,7 @@ svn_editor_add_directory(svn_editor_t *e
#endif
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -469,6 +529,7 @@ svn_editor_add_file(svn_editor_t *editor
SVN_ERR_ASSERT(props != NULL);
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, relpath);
CHECK_UNKNOWN_CHILD(editor, relpath);
SVN_ERR(check_cancel(editor));
@@ -483,10 +544,11 @@ svn_editor_add_file(svn_editor_t *editor
}
MARK_COMPLETED(editor, relpath);
+ MARK_PARENT_STABLE(editor, relpath);
CLEAR_INCOMPLETE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -503,6 +565,7 @@ svn_editor_add_symlink(svn_editor_t *edi
SVN_ERR_ASSERT(props != NULL);
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, relpath);
CHECK_UNKNOWN_CHILD(editor, relpath);
SVN_ERR(check_cancel(editor));
@@ -516,10 +579,11 @@ svn_editor_add_symlink(svn_editor_t *edi
}
MARK_COMPLETED(editor, relpath);
+ MARK_PARENT_STABLE(editor, relpath);
CLEAR_INCOMPLETE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -534,6 +598,7 @@ svn_editor_add_absent(svn_editor_t *edit
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, relpath);
CHECK_UNKNOWN_CHILD(editor, relpath);
SVN_ERR(check_cancel(editor));
@@ -547,10 +612,11 @@ svn_editor_add_absent(svn_editor_t *edit
}
MARK_COMPLETED(editor, relpath);
+ MARK_PARENT_STABLE(editor, relpath);
CLEAR_INCOMPLETE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -558,14 +624,17 @@ svn_error_t *
svn_editor_alter_directory(svn_editor_t *editor,
const char *relpath,
svn_revnum_t revision,
+ const apr_array_header_t *children,
apr_hash_t *props)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
- SVN_ERR_ASSERT(props != NULL);
+ SVN_ERR_ASSERT(children != NULL || props != NULL);
+ /* ### validate children are just basenames? */
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ALTER(editor, relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, relpath);
SVN_ERR(check_cancel(editor));
@@ -573,15 +642,36 @@ svn_editor_alter_directory(svn_editor_t
{
START_CALLBACK(editor);
err = editor->funcs.cb_alter_directory(editor->baton,
- relpath, revision, props,
+ relpath, revision,
+ children, props,
editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_COMPLETED(editor, relpath);
+ MARK_PARENT_STABLE(editor, relpath);
+
+#ifdef ENABLE_ORDERING_CHECK
+ /* ### this is not entirely correct. we probably need to adjust the
+ ### check_unknown_child() function for this scenario. */
+#if 0
+ {
+ int i;
+ for (i = 0; i < children->nelts; i++)
+ {
+ const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
+ const char *child = svn_relpath_join(relpath, child_basename,
+ editor->state_pool);
+
+ apr_hash_set(editor->pending_incomplete_children, child,
+ APR_HASH_KEY_STRING, "");
+ }
+ }
+#endif
+#endif
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -603,6 +693,7 @@ svn_editor_alter_file(svn_editor_t *edit
SVN_ERR_ASSERT(checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ALTER(editor, relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, relpath);
SVN_ERR(check_cancel(editor));
@@ -617,9 +708,10 @@ svn_editor_alter_file(svn_editor_t *edit
}
MARK_COMPLETED(editor, relpath);
+ MARK_PARENT_STABLE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -636,6 +728,7 @@ svn_editor_alter_symlink(svn_editor_t *e
SVN_ERR_ASSERT(props != NULL || target != NULL);
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ALTER(editor, relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, relpath);
SVN_ERR(check_cancel(editor));
@@ -650,9 +743,10 @@ svn_editor_alter_symlink(svn_editor_t *e
}
MARK_COMPLETED(editor, relpath);
+ MARK_PARENT_STABLE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -666,6 +760,8 @@ svn_editor_delete(svn_editor_t *editor,
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_NOT_BE_COMPLETED(editor, relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, relpath);
+ CHILD_DELETIONS_ALLOWED(editor, relpath);
SVN_ERR(check_cancel(editor));
@@ -678,9 +774,10 @@ svn_editor_delete(svn_editor_t *editor,
}
MARK_COMPLETED(editor, relpath);
+ MARK_PARENT_STABLE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -697,6 +794,8 @@ svn_editor_copy(svn_editor_t *editor,
SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, dst_relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
SVN_ERR(check_cancel(editor));
@@ -710,10 +809,11 @@ svn_editor_copy(svn_editor_t *editor,
}
MARK_ALLOW_ALTER(editor, dst_relpath);
+ MARK_PARENT_STABLE(editor, dst_relpath);
CLEAR_INCOMPLETE(editor, dst_relpath);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -731,6 +831,9 @@ svn_editor_move(svn_editor_t *editor,
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_NOT_BE_COMPLETED(editor, src_relpath);
SHOULD_ALLOW_ADD(editor, dst_relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
+ CHILD_DELETIONS_ALLOWED(editor, src_relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
SVN_ERR(check_cancel(editor));
@@ -744,11 +847,13 @@ svn_editor_move(svn_editor_t *editor,
}
MARK_ALLOW_ADD(editor, src_relpath);
+ MARK_PARENT_STABLE(editor, src_relpath);
MARK_ALLOW_ALTER(editor, dst_relpath);
+ MARK_PARENT_STABLE(editor, dst_relpath);
CLEAR_INCOMPLETE(editor, dst_relpath);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -769,6 +874,8 @@ svn_editor_rotate(svn_editor_t *editor,
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SHOULD_NOT_BE_COMPLETED(editor, relpath);
+ VERIFY_PARENT_MAY_EXIST(editor, relpath);
+ CHILD_DELETIONS_ALLOWED(editor, relpath);
}
}
#endif
@@ -790,12 +897,13 @@ svn_editor_rotate(svn_editor_t *editor,
{
const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *);
MARK_ALLOW_ALTER(editor, relpath);
+ MARK_PARENT_STABLE(editor, relpath);
}
}
#endif
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -819,7 +927,7 @@ svn_editor_complete(svn_editor_t *editor
MARK_FINISHED(editor);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
@@ -840,5 +948,5 @@ svn_editor_abort(svn_editor_t *editor)
MARK_FINISHED(editor);
svn_pool_clear(editor->scratch_pool);
- return err;
+ return svn_error_trace(err);
}
Modified: subversion/branches/inheritable-props/subversion/libsvn_delta/path_driver.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_delta/path_driver.c?rev=1354186&r1=1354185&r2=1354186&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_delta/path_driver.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_delta/path_driver.c Tue Jun 26 19:26:49 2012
@@ -44,15 +44,13 @@ typedef struct dir_stack_t
} dir_stack_t;
-/* Call EDITOR's open_directory() function with the PATH and REVISION
- * arguments, and then add the resulting dir baton to the dir baton
- * stack.
+/* Call EDITOR's open_directory() function with the PATH argument, then
+ * add the resulting dir baton to the dir baton stack.
*/
static svn_error_t *
open_dir(apr_array_header_t *db_stack,
const svn_delta_editor_t *editor,
const char *path,
- svn_revnum_t revision,
apr_pool_t *pool)
{
void *parent_db, *db;
@@ -69,7 +67,8 @@ open_dir(apr_array_header_t *db_stack,
/* Call the EDITOR's open_directory function to get a new directory
baton. */
subpool = svn_pool_create(pool);
- SVN_ERR(editor->open_directory(path, parent_db, revision, subpool, &db));
+ SVN_ERR(editor->open_directory(path, parent_db, SVN_INVALID_REVNUM, subpool,
+ &db));
/* Now add the dir baton to the stack. */
item = apr_pcalloc(subpool, sizeof(*item));
@@ -131,13 +130,12 @@ count_components(const char *path)
/*** Public interfaces ***/
svn_error_t *
-svn_delta_path_driver(const svn_delta_editor_t *editor,
- void *edit_baton,
- svn_revnum_t revision,
- const apr_array_header_t *paths,
- svn_delta_path_driver_cb_func_t callback_func,
- void *callback_baton,
- apr_pool_t *pool)
+svn_delta_path_driver2(const svn_delta_editor_t *editor,
+ void *edit_baton,
+ const apr_array_header_t *paths,
+ svn_delta_path_driver_cb_func_t callback_func,
+ void *callback_baton,
+ apr_pool_t *pool)
{
apr_array_header_t *db_stack = apr_array_make(pool, 4, sizeof(void *));
const char *last_path = NULL;
@@ -155,9 +153,6 @@ svn_delta_path_driver(const svn_delta_ed
iterpool = svn_pool_create(pool);
item = apr_pcalloc(subpool, sizeof(*item));
- /* Sort the paths in a depth-first directory-ish order. */
- qsort(paths->elts, paths->nelts, paths->elt_size, svn_sort_compare_paths);
-
/* If the root of the edit is also a target path, we want to call
the callback function to let the user open the root directory and
do what needs to be done. Otherwise, we'll do the open_root()
@@ -171,7 +166,7 @@ svn_delta_path_driver(const svn_delta_ed
}
else
{
- SVN_ERR(editor->open_root(edit_baton, revision, subpool, &db));
+ SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, subpool, &db));
}
item->pool = subpool;
item->dir_baton = db;
@@ -238,7 +233,7 @@ svn_delta_path_driver(const svn_delta_ed
rel = apr_pstrmemdup(iterpool, pdir, piece - pdir);
/* Open the subdirectory. */
- SVN_ERR(open_dir(db_stack, editor, rel, revision, pool));
+ SVN_ERR(open_dir(db_stack, editor, rel, pool));
/* If we found a '/', advance our PIECE pointer to
character just after that '/'. Otherwise, we're