subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From julianf...@apache.org
Subject svn commit: r1639592 [4/4] - in /subversion/branches/move-tracking-2/subversion: include/private/svn_editor3.h libsvn_delta/branching.c libsvn_delta/compat3.c libsvn_delta/compat3b.c
Date Fri, 14 Nov 2014 10:05:41 GMT
Copied: subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3b.c (from r1639314, subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c)
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3b.c?p2=subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3b.c&p1=subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c&r1=1639314&r2=1639592&rev=1639592&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3b.c Fri Nov 14 10:05:32 2014
@@ -1,5 +1,5 @@
 /*
- * compat3.c : Ev3-to-Ev1 compatibility.
+ * compat3b.c : Ev3-to-Ev1 compatibility via element-based branching.
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -33,7 +33,6 @@
 #include "svn_pools.h"
 
 #include "private/svn_delta_private.h"
-#include "private/svn_sorts_private.h"
 #include "../libsvn_delta/debug_editor.h"
 #include "svn_private_config.h"
 
@@ -52,120 +51,6 @@ peg_path_str(svn_editor3_peg_path_t loc,
 }
 #endif
 
-/*
- * ========================================================================
- * Configuration Options
- * ========================================================================
- */
-
-/* Features that are not wanted for this commit editor shim but may be
- * wanted in a similar but different shim such as for an update editor. */
-/* #define SHIM_WITH_ADD_ABSENT */
-/* #define SHIM_WITH_UNLOCK */
-
-/* The Ev2 shim ran the accumulated actions during abort... But why?
- * If we don't, then aborting and re-opening a commit txn doesn't find
- * all the previous changes, so tests/libsvn_repos/repos-test 12 fails.
- */
-#define SHIM_WITH_ACTIONS_DURING_ABORT
-
-/* Whether to support switching from relative to absolute paths in the
- * Ev1 methods. */
-/* #define SHIM_WITH_ABS_PATHS */
-
-
-/*
- * ========================================================================
- * Shim Connector
- * ========================================================================
- *
- * The shim connector enables a more exact round-trip conversion from an
- * Ev1 drive to Ev3 and back to Ev1.
- */
-struct svn_editor3__shim_connector_t
-{
-  /* Set to true if and when an Ev1 receiving shim receives an absolute
-   * path (prefixed with '/') from the delta edit, and causes the Ev1
-   * sending shim to send absolute paths.
-   * ### NOT IMPLEMENTED
-   */
-#ifdef SHIM_WITH_ABS_PATHS
-  svn_boolean_t *ev1_absolute_paths;
-#endif
-
-  /* The Ev1 set_target_revision and start_edit methods, respectively, will
-   * call the TARGET_REVISION_FUNC and START_EDIT_FUNC callbacks, if non-null.
-   * Otherwise, default calls will be used.
-   *
-   * (Possibly more useful for update editors than for commit editors?) */
-  svn_editor3__set_target_revision_func_t target_revision_func;
-
-  /* If not null, a callback that the Ev3 driver may call to
-   * provide the "base revision" of the root directory, even if it is not
-   * going to modify that directory. (If it does modify it, then it will
-   * pass in the appropriate base revision at that time.) If null
-   * or if the driver does not call it, then the Ev1
-   * open_root() method will be called with SVN_INVALID_REVNUM as the base
-   * revision parameter.
-   */
-  svn_delta__start_edit_func_t start_edit_func;
-
-#ifdef SHIM_WITH_UNLOCK
-  /* A callback which will be called when an unlocking action is received.
-     (For update editors?) */
-  svn_delta__unlock_func_t unlock_func;
-#endif
-
-  void *baton;
-};
-
-svn_error_t *
-svn_editor3__insert_shims(
-                        const svn_delta_editor_t **new_deditor,
-                        void **new_dedit_baton,
-                        const svn_delta_editor_t *old_deditor,
-                        void *old_dedit_baton,
-                        const char *repos_root,
-                        const char *base_relpath,
-                        svn_editor3__shim_fetch_func_t fetch_func,
-                        void *fetch_baton,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
-{
-  svn_editor3_t *editor3;
-  svn_editor3__shim_connector_t *shim_connector;
-
-#ifdef SVN_DEBUG
-  /*SVN_ERR(svn_delta__get_debug_editor(&old_deditor, &old_dedit_baton,
-                                      old_deditor, old_dedit_baton,
-                                      "[OUT] ", result_pool));*/
-#endif
-  SVN_ERR(svn_delta__ev3_from_delta_for_commit(
-                        &editor3,
-                        &shim_connector,
-                        old_deditor, old_dedit_baton,
-                        repos_root, base_relpath,
-                        fetch_func, fetch_baton,
-                        NULL, NULL /*cancel*/,
-                        result_pool, scratch_pool));
-#ifdef SVN_DEBUG
-  /*SVN_ERR(svn_editor3__get_debug_editor(&editor3, editor3, result_pool));*/
-#endif
-  SVN_ERR(svn_delta__delta_from_ev3_for_commit(
-                        new_deditor, new_dedit_baton,
-                        editor3,
-                        repos_root, base_relpath,
-                        fetch_func, fetch_baton,
-                        shim_connector,
-                        result_pool, scratch_pool));
-#ifdef SVN_DEBUG
-  /*SVN_ERR(svn_delta__get_debug_editor(new_deditor, new_dedit_baton,
-                                      *new_deditor, *new_dedit_baton,
-                                      "[IN]  ", result_pool));*/
-#endif
-  return SVN_NO_ERROR;
-}
-
 
 /*
  * ========================================================================
@@ -367,8 +252,8 @@ delete_subtree(apr_hash_t *changes,
     {
       /* If this previous change was a non-replacing addition, there
          is no longer any change to be made at this path. If it was
-         a replacement or a modification, it now becomes a delete. (If it was a delete,
-         this attempt to delete is an error.) */
+         a replacement or a modification, it now becomes a delete.
+         (If it was a delete, this attempt to delete is an error.) */
        VERIFY(change->action != RESTRUCTURE_DELETE);
        if (change->action == RESTRUCTURE_ADD && !change->deleting)
          {
@@ -412,2860 +297,49 @@ delete_subtree(apr_hash_t *changes,
   return SVN_NO_ERROR;
 }
 
-/* Insert a new change for RELPATH, or return an existing one.
- *
- * Verify Ev1 ordering.
- *
- * RELPATH is relative to the repository root.
- */
-static svn_error_t *
-insert_change_ev1_rules(change_node_t **change_p, apr_hash_t *changes,
-                        const char *relpath,
-                        enum restructure_action_t action,
-                        svn_node_kind_t kind)
-{
-  apr_pool_t *changes_pool = apr_hash_pool_get(changes);
-  apr_pool_t *scratch_pool = changes_pool;
-  change_node_t *change = svn_hash_gets(changes, relpath);
-  svn_tristate_t exists = check_existence(changes, relpath);
-
-  /* Check whether this op is allowed. */
-  switch (action)
-    {
-    case RESTRUCTURE_NONE:
-      VERIFY(kind == svn_node_dir || kind == svn_node_file);
-      VERIFY(exists != svn_tristate_false);
-      if (change)
-        {
-          VERIFY(change->kind == kind);
-        }
-      break;
-
-    case RESTRUCTURE_ADD:
-      VERIFY(kind == svn_node_dir || kind == svn_node_file);
-      if (change)
-        {
-          /* Add or copy is allowed after delete (and replaces the delete),
-           * but not allowed after an add or a no-restructure change. */
-          VERIFY(change->action == RESTRUCTURE_DELETE);
-        }
-      else
-        {
-          const char *parent_path = svn_relpath_dirname(relpath, scratch_pool);
-
-          /* Disallow if *parent* path is known to be non-existent
-           * (deleted (root or child), or child of a non-copy add). */
-          VERIFY(check_existence(changes, parent_path) != svn_tristate_false);
-        }
-      break;
-
-#ifdef SHIM_WITH_ADD_ABSENT
-    case RESTRUCTURE_ADD_ABSENT:
-      VERIFY(kind == svn_node_dir || kind == svn_node_file);
-      /* ### */
-      break;
-#endif
-
-    case RESTRUCTURE_DELETE:
-      VERIFY(kind == svn_node_none);
-      /* Delete is allowed only on a currently existing path. */
-      VERIFY(exists != svn_tristate_false);
-      /* Remove change records for any child paths inside this delete. */
-      {
-        apr_hash_index_t *hi;
-
-        for (hi = apr_hash_first(scratch_pool, changes);
-             hi; hi = apr_hash_next(hi))
-          {
-            const char *this_relpath = apr_hash_this_key(hi);
-            const char *r = svn_relpath_skip_ancestor(relpath, this_relpath);
-
-            if (r && r[0])
-              {
-                svn_hash_sets(changes, this_relpath, NULL);
-              }
-          }
-      }
-
-      break;
-    }
-
-  if (change)
-    {
-      if (action != RESTRUCTURE_NONE)
-        {
-          change->action = action;
-        }
-      change->kind = kind;
-    }
-  else
-    {
-      /* Create a new change. Callers will set the other fields as needed. */
-      change = apr_pcalloc(changes_pool, sizeof(*change));
-      change->action = action;
-      change->kind = kind;
-      change->changing_rev = SVN_INVALID_REVNUM;
-      change->deleting_rev = SVN_INVALID_REVNUM;
-
-      svn_hash_sets(changes, apr_pstrdup(changes_pool, relpath), change);
-    }
-
-  *change_p = change;
-  return SVN_NO_ERROR;
-}
-
-/* Duplicate any child changes from the subtree under (but excluding)
- * FROM_PATH into the subtree under (but excluding) NEW_PATH. */
-static svn_error_t *
-duplicate_child_changes(apr_hash_t *changes,
-                        const char *from_path,
-                        const char *new_path,
-                        apr_pool_t *scratch_pool)
-{
-  apr_pool_t *changes_pool = apr_hash_pool_get(changes);
-  apr_hash_index_t *hi;
-
-  /* for each change ... */
-  for (hi = apr_hash_first(scratch_pool, changes);
-       hi; hi = apr_hash_next(hi))
-    {
-      const char *this_path = apr_hash_this_key(hi);
-      change_node_t *this_change = apr_hash_this_val(hi);
-      const char *r = svn_relpath_skip_ancestor(from_path, this_path);
-
-      /* ... at a child path strictly below FROM_PATH ... */
-      if (r && r[0])
-        {
-          const char *new_child_path
-            = svn_relpath_join(new_path, r, changes_pool);
-
-          /* ... duplicate that change as a child of NEW_PATH */
-          svn_hash_sets(changes, new_child_path, this_change);
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
 
 /*
  * ===================================================================
- * Commit Editor converter to join a v1 driver to a v3 consumer
+ * Commit Editor converter to join a v3 driver to a v1 consumer
  * ===================================================================
  *
- * The following code maps the calls to a traditional delta editor to an
- * Editor v3.
- *
- * It does not create 'move' operations, neither heuristically nor using
- * out-of-band cues. In fact, the code structure is likely to be
- * unsuitable for processing moves.
+ * This editor buffers all the changes before driving the Ev1 at the end,
+ * since it needs to do a single depth-first traversal of the path space
+ * and this cannot be started until all moves are known.
  *
- * The design assumes that each Ev1 path maps to a different Ev3 element.
+ * Moves are converted to copy-and-delete, with the copy being from
+ * the source peg rev. (### Should it request copy-from revision "-1"?)
  *
  * It works like this:
  *
  *                +------+--------+
  *                | path | change |
- *      Ev1  -->  +------+--------+  -->  Ev3
+ *      Ev3  -->  +------+--------+  -->  Ev1
  *                | ...  | ...    |
  *                | ...  | ...    |
  *
- *   1. Ev1 changes are accumulated in a per-path table, EB->changes.
- *      Changes are de-duplicated so there is only one change per path.
- *
- *   2. On Ev1 close-edit, walk through the table in a depth-first order,
- *      sending the equivalent Ev3 action for each change.
- *
- * ### This was designed (in its Ev2 form) for both commit and update
- *     editors, but Ev3 is currently only designed as a commit editor.
- *     Therefore 'update' functionality probably doesn't work, including:
- *       - create 'absent' node (currently just omits the 'put', which
- *           Ev3 currently defines will create an empty node)
- *
- * ### Need to review all revisions passed to pathrev()/txn_path()
- *     constructors: are they really the right peg revs?
- *
- * TODO: Fetch the base (kind, props, text) of an opened file or dir
- * right away when it's opened. Delaying the fetch, as we do for the
- * sake of 'optimization', adds complexity & is probably a poor choice.
- */
-
-/* Construct a peg-path-rev */
-static svn_editor3_peg_path_t
-pathrev(const char *repos_relpath, svn_revnum_t revision)
-{
-  svn_editor3_peg_path_t p;
-
-  p.rev = revision;
-  p.relpath = repos_relpath;
-  return p;
-}
-
-/* Construct a txn-path-rev */
-static svn_editor3_txn_path_t
-txn_path(const char *repos_relpath, svn_revnum_t revision,
-         const char *created_relpath)
-{
-  svn_editor3_txn_path_t p;
-
-  p.peg.rev = revision;
-  p.peg.relpath = repos_relpath;
-  p.relpath = created_relpath;
-  return p;
-}
-
-struct ev3_edit_baton
-{
-  svn_editor3_t *editor;
-
-  apr_hash_t *changes;  /* REPOS_RELPATH -> struct change_node  */
-
-  /* (const char *) paths relative to repository root, in path visiting
-     order. */
-  apr_array_header_t *path_order;
-#ifdef SHIM_WITH_ACTIONS_DURING_ABORT
-  /* Number of paths in PATH_ORDER processed so far. */
-  int paths_processed;
-#endif
-
-  /* Repository root URL. */
-  const char *repos_root_url;
-  /* Base directory of the edit, relative to the repository root. */
-  const char *base_relpath;
-
-  const svn_editor3__shim_connector_t *shim_connector;
-
-  svn_editor3__shim_fetch_func_t fetch_func;
-  void *fetch_baton;
-
-  svn_boolean_t closed;
-
-  apr_pool_t *edit_pool;
-};
-
-struct ev3_dir_baton
-{
-  struct ev3_edit_baton *eb;
-
-  /* Path of this directory relative to repository root. */
-  const char *path;
-  /* The base revision if this is a pre-existing directory;
-     SVN_INVALID_REVNUM if added/copied.
-     ### Can also be SVN_INVALID_REVNUM for a pre-existing dir,
-         meaning the base is the youngest revision. */
-  svn_revnum_t base_revision;
-
-  /* Copy-from path (relative to repository root) and revision. This is
-     set for each dir inside a copy, not just the copy root. */
-  const char *copyfrom_relpath;
-  svn_revnum_t copyfrom_rev;
-};
-
-struct ev3_file_baton
-{
-  struct ev3_edit_baton *eb;
-
-  /* Path of this file relative to repository root. */
-  const char *path;
-  /* The base revision if this is a pre-existing file;
-     SVN_INVALID_REVNUM if added/copied.
-     ### Can also be SVN_INVALID_REVNUM for a pre-existing file,
-         meaning the base is the youngest revision. */
-  svn_revnum_t base_revision;
-
-  /* Copy-from path (relative to repository root) and revision. This is
-     set for each file inside a copy, not just the copy root. */
-  const char *copyfrom_relpath;
-  svn_revnum_t copyfrom_rev;
-
-  /* Path to a file containing the base text. */
-  svn_stringbuf_t *delta_base_text;
-};
-
-/* Return the 'txn_path' that addresses the node that is currently at
- * RELPATH according to the info in CHANGES.
- *
- * One way to describe it:
- *
- *   If relpath is a created path:
- *     find_txn_path(its parent)
- *     add (its basename) to the created-path part
- *   elif relpath is an already-existing path:
- *     return txn_path(relpath, base rev, "")
- *   else: # it's deleted
- *     return None
+ *   1. Ev3 changes are accumulated in a per-path table, EB->changes.
  *
- * Another way:
+ *   2. On Ev3 close-edit, walk through the table in a depth-first order,
+ *      sending the equivalent Ev1 action for each change.
  *
- *   p := first path component that is add/copy, starting from root
- *   d := dirname(p)
- *   return txn_path(d, changing_rev(d), remainder-relpath)
- */
-static svn_editor3_txn_path_t
-find_txn_path(apr_hash_t *changes,
-              const char *relpath,
-              apr_pool_t *result_pool)
-{
-  const char *existing_path = "", *remainder_path = relpath;
-  svn_revnum_t existing_revnum = SVN_INVALID_REVNUM;
-  int i;
-  svn_editor3_txn_path_t loc;
-
-  /* The root path was necessarily existing. For each further path component,
-     if it was existing, add it to the 'existing path', else stop there. */
-  for (i = 1; *remainder_path; i++)
-    {
-      const char *new_prefix_path = svn_relpath_limit(relpath, i, result_pool);
-      change_node_t *change = svn_hash_gets(changes, new_prefix_path);
-
-      if (change && change->action == RESTRUCTURE_ADD)
-        break;
-
-      existing_path = new_prefix_path;
-      remainder_path = svn_relpath_skip_ancestor(existing_path, relpath);
-      if (change && change->action == RESTRUCTURE_NONE)
-        /* ### This is all well and good when there is a RESTRUCTURE_NONE
-               change recorded for this dir, but for an unchanged parent
-               dir we don't know what the base revision was ... unless we
-               record every 'opened' parent dir. Should we do that? */
-        existing_revnum = change->changing_rev;
-    }
-  loc = txn_path(existing_path, existing_revnum, remainder_path);
-  return loc;
-}
-
-/* Drive the Ev3 EB->editor to make the Ev1-style edits described by
- * CHANGE for the path REPOS_RELPATH.
+ * TODO
  *
- * Note: We do not support converting copy-and-delete to send an Ev3 move.
- * This per-path model of processing is not well suited to doing so.
- */
-static svn_error_t *
-process_actions(struct ev3_edit_baton *eb,
-                const char *repos_relpath,
-                const change_node_t *change,
-                apr_pool_t *scratch_pool)
-{
-  svn_editor3_txn_path_t change_loc
-    = find_txn_path(eb->changes, repos_relpath, scratch_pool);
-  const char *repos_relpath_dirname = svn_relpath_dirname(repos_relpath, scratch_pool);
-  const char *repos_relpath_basename = svn_relpath_basename(repos_relpath, scratch_pool);
-
-  SVN_ERR_ASSERT(change != NULL);
-
-#ifdef SHIM_WITH_UNLOCK
-  if (eb->shim_connector && change->unlock)
-    SVN_ERR(eb->shim_connector->unlock_func(
-              eb->shim_connector->baton, repos_relpath, scratch_pool));
-#endif
-
-  /* Process any delete, no matter whether it will be replaced. */
-  if (change->deleting)
-    {
-      SVN_ERR(svn_editor3_rm(eb->editor,
-                             txn_path(repos_relpath, change->deleting_rev, "")));
-    }
-
-#ifdef SHIM_WITH_ADD_ABSENT
-  if (change->action == RESTRUCTURE_ADD_ABSENT)
-    {
-      SVN_ERR(svn_editor3_mk(eb->editor, change->kind,
-                             parent_loc, repos_relpath_basename));
-
-      /* No further work possible on this path. */
-      return SVN_NO_ERROR;
-    }
-#endif
-
-  if (change->action == RESTRUCTURE_ADD)
-    {
-      svn_editor3_txn_path_t parent_loc
-        = find_txn_path(eb->changes, repos_relpath_dirname, scratch_pool);
-
-      if (change->copyfrom_path != NULL)
-        {
-          SVN_ERR(svn_editor3_cp(eb->editor,
-#ifdef SVN_EDITOR3_WITH_COPY_FROM_THIS_REV
-                                 txn_path(change->copyfrom_path, change->copyfrom_rev, ""),
-#else
-                                 pathrev(change->copyfrom_path, change->copyfrom_rev),
-#endif
-                                 parent_loc, repos_relpath_basename));
-          /* Fall through to possibly make changes post-copy.  */
-        }
-      else
-        {
-          SVN_ERR(svn_editor3_mk(eb->editor, change->kind,
-                                 parent_loc, repos_relpath_basename));
-          /* Fall through to make changes post-add. */
-        }
-    }
-
-  if (change->props || change->contents_changed)
-    {
-      svn_editor3_node_content_t *new_content;
-
-      if (change->kind == svn_node_file)
-        {
-          svn_stringbuf_t *text;
-
-          if (change->contents_text)
-            {
-              /*SVN_DBG(("contents_changed=%d, contents_text='%.20s...'",
-                       change->contents_changed, change->contents_text->data));*/
-              text = change->contents_text;
-            }
-          else
-            {
-              SVN_DBG(("file '%s', no content, act=%d, cp=%s@%ld",
-                       repos_relpath, change->action, change->copyfrom_path, change->copyfrom_rev));
-              /*### not: VERIFY(change->action == RESTRUCTURE_ADD && ! change->copyfrom_path);*/
-
-              /* If this file was added, but apply_txdelta() was not called (i.e.
-                 CONTENTS_CHANGED is FALSE), we're adding an empty file. */
-              text = svn_stringbuf_create_empty(scratch_pool);
-            }
-
-          new_content = svn_editor3_node_content_create_file(
-                          change->props, text, scratch_pool);
-        }
-      else if (change->kind == svn_node_dir)
-        {
-          new_content = svn_editor3_node_content_create_dir(
-                          change->props, scratch_pool);
-        }
-      else
-        SVN_ERR_MALFUNCTION();
-      SVN_ERR(svn_editor3_put(eb->editor, change_loc, new_content));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*  */
-static svn_error_t *
-run_actions(struct ev3_edit_baton *eb,
-            apr_pool_t *scratch_pool)
-{
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-  int i;
-
-#ifdef SHIM_WITH_ACTIONS_DURING_ABORT
-  /* Possibly pick up where we left off. Occasionally, we do some of these
-     as part of close_edit() and then some more as part of abort_edit()  */
-  for (i = eb->paths_processed; i < eb->path_order->nelts; ++i, ++eb->paths_processed)
-#else
-  for (i = 0; i < eb->path_order->nelts; ++i)
-#endif
-    {
-      const char *repos_relpath
-        = APR_ARRAY_IDX(eb->path_order, i, const char *);
-      const change_node_t *change = svn_hash_gets(eb->changes, repos_relpath);
-
-      /* Process the change for each path only once, no
-         matter how many times the Ev1 driver visited it. When we've
-         processed a path successfully, remove it from the queue. */
-      if (change)
-        {
-          svn_pool_clear(iterpool);
-
-          SVN_ERR(process_actions(eb, repos_relpath, change, iterpool));
-
-          /* Remove the action, as we've now processed it. */
-          svn_hash_sets(eb->changes, repos_relpath, NULL);
-        }
-    }
-  svn_pool_destroy(iterpool);
-
-  return SVN_NO_ERROR;
-}
-
-/* Return the repository-relative path for a given Ev1 input path (that is,
- * a relpath-within-edit or a URL). */
-static const char *
-map_to_repos_relpath(struct ev3_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_url, 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);
-    }
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_set_target_revision(void *edit_baton,
-                        svn_revnum_t target_revision,
-                        apr_pool_t *scratch_pool)
-{
-  struct ev3_edit_baton *eb = edit_baton;
-
-  if (eb->shim_connector && eb->shim_connector->target_revision_func)
-    SVN_ERR(eb->shim_connector->target_revision_func(
-              eb->shim_connector->baton, target_revision, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_open_root(void *edit_baton,
-              svn_revnum_t base_revision,
-              apr_pool_t *result_pool,
-              void **root_baton)
-{
-  struct ev3_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db));
-  struct ev3_edit_baton *eb = edit_baton;
-
-  db->eb = eb;
-  db->path = apr_pstrdup(eb->edit_pool, eb->base_relpath);
-  db->base_revision = base_revision;
-
-  *root_baton = db;
-
-  if (eb->shim_connector && eb->shim_connector->start_edit_func)
-    SVN_ERR(eb->shim_connector->start_edit_func(
-              eb->shim_connector->baton, base_revision));
-
-  return SVN_NO_ERROR;
-}
-
-/* Record a property change in the (existing or new) change record for
- * the node at RELPATH of kind KIND. Change property NAME to value VALUE,
- * or delete the property if VALUE is NULL.
- *
- * Fetch and store the base properties for this node, using the callback
- * EB->fetch_props_func/baton, if we have not yet done so. Then apply the
- * edit to those base properties or to the set of properties resulting
- * from the previous edit.
- *
- * BASE_REVISION is the base revision of the node that is currently at
- * RELPATH, or SVN_INVALID_REVNUM for an added/copied node. COPYFROM_PATH
- * and COPYFROM_REV are the base location for a copied node, including a
- * child of a copy.
- *
- * RELPATH is relative to the repository root.
- */
-static svn_error_t *
-apply_propedit(struct ev3_edit_baton *eb,
-               const char *relpath,
-               svn_node_kind_t kind,
-               svn_revnum_t base_revision,
-               const char *copyfrom_path,
-               svn_revnum_t copyfrom_rev,
-               const char *name,
-               const svn_string_t *value,
-               apr_pool_t *scratch_pool)
-{
-  change_node_t *change;
-
-  SVN_ERR(insert_change_ev1_rules(&change, eb->changes, relpath,
-                                  RESTRUCTURE_NONE, kind));
-
-  /* Record the observed order. */
-  APR_ARRAY_PUSH(eb->path_order, const char *)
-    = apr_pstrdup(eb->edit_pool, relpath);
-
-  /* We're changing the node, so record the base revision in case this is
-     the first change. (But we don't need to fill in the copy-from, as a
-     change entry would already have been recorded for a copy-root.) */
-  VERIFY(!SVN_IS_VALID_REVNUM(change->changing_rev)
-         || change->changing_rev == base_revision);
-  change->changing_rev = base_revision;
-
-  /* Fetch the original set of properties, if we haven't done so yet. */
-  if (change->props == NULL)
-    {
-      /* If this is a copied/moved node, 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 (copyfrom_path)
-        {
-          SVN_ERR(eb->fetch_func(NULL, &change->props, NULL, NULL,
-                                 eb->fetch_baton,
-                                 copyfrom_path, copyfrom_rev,
-                                 eb->edit_pool, scratch_pool));
-          SVN_DBG(("apply_propedit('%s@%ld'): fetched %d copy-from props (from %s@%ld)",
-                   relpath, base_revision, apr_hash_count(change->props),
-                   copyfrom_path, copyfrom_rev));
-        }
-      else if (change->action == RESTRUCTURE_ADD)
-        {
-          change->props = apr_hash_make(eb->edit_pool);
-        }
-      else
-        {
-          if (! SVN_IS_VALID_REVNUM(base_revision))
-            SVN_DBG(("apply_propedit('%s@%ld')  ### need to resolve to HEAD?",
-                     relpath, base_revision));
-          SVN_ERR(eb->fetch_func(NULL, &change->props, NULL, NULL,
-                                 eb->fetch_baton,
-                                 relpath, base_revision,
-                                 eb->edit_pool, scratch_pool));
-          SVN_DBG(("apply_propedit('%s@%ld'): fetched %d original props",
-                   relpath, base_revision, apr_hash_count(change->props)));
-        }
-    }
-
-  if (value == NULL)
-    svn_hash_sets(change->props, name, NULL);
-  else
-    svn_hash_sets(change->props,
-                  apr_pstrdup(eb->edit_pool, name),
-                  svn_string_dup(value, eb->edit_pool));
-  SVN_DBG(("apply_propedit('%s@%ld'): set prop %s=%.50s",
-           relpath, base_revision, name, value ? value->data : "<nil>"));
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_delete_entry(const char *path,
-                 svn_revnum_t revision,
-                 void *parent_baton,
-                 apr_pool_t *scratch_pool)
-{
-  struct ev3_dir_baton *pb = parent_baton;
-  svn_revnum_t base_revision;
-  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
-  change_node_t *change;
-
-  SVN_ERR(insert_change_ev1_rules(&change, pb->eb->changes, relpath,
-                                  RESTRUCTURE_DELETE, svn_node_none));
-
-  /* Record the observed order. */
-  APR_ARRAY_PUSH(pb->eb->path_order, const char *)
-    = apr_pstrdup(pb->eb->edit_pool, relpath);
-
-  if (SVN_IS_VALID_REVNUM(revision))
-    base_revision = revision;
-  else
-    base_revision = pb->base_revision;
-  /* ### Shouldn't base_revision be SVN_INVALID_REVNUM instead, if the
-         node to delete was created (added/copied) in this edit? */
-
-  /* ### Should these checks be in insert_change()? */
-  VERIFY(!change->deleting || change->deleting_rev == base_revision);
-  change->deleting = TRUE;
-  change->deleting_rev = base_revision;
-  if (!SVN_IS_VALID_REVNUM(base_revision))
-    SVN_DBG(("ev3_delete_entry('%s'): deleting_rev = base_revision == -1", path));
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_add_directory(const char *path,
-                  void *parent_baton,
-                  const char *copyfrom_path,
-                  svn_revnum_t copyfrom_revision,
-                  apr_pool_t *result_pool,
-                  void **child_baton)
-{
-  apr_pool_t *scratch_pool = result_pool;
-  struct ev3_dir_baton *pb = parent_baton;
-  struct ev3_dir_baton *cb = apr_pcalloc(result_pool, sizeof(*cb));
-  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
-  change_node_t *change;
-
-  SVN_ERR(insert_change_ev1_rules(&change, pb->eb->changes, relpath,
-                                  RESTRUCTURE_ADD, svn_node_dir));
-
-  /* Record the observed order. */
-  APR_ARRAY_PUSH(pb->eb->path_order, const char *)
-    = apr_pstrdup(pb->eb->edit_pool, relpath);
-
-  cb->eb = pb->eb;
-  cb->path = apr_pstrdup(result_pool, relpath);
-  cb->base_revision = SVN_INVALID_REVNUM;
-  *child_baton = cb;
-
-  if (!copyfrom_path)
-    {
-      if (pb->copyfrom_relpath)
-        {
-          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 */
-
-      change->copyfrom_path = map_to_repos_relpath(pb->eb, copyfrom_path,
-                                                   pb->eb->edit_pool);
-      change->copyfrom_rev = copyfrom_revision;
-
-      cb->copyfrom_relpath = change->copyfrom_path;
-      cb->copyfrom_rev = change->copyfrom_rev;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_open_directory(const char *path,
-                   void *parent_baton,
-                   svn_revnum_t base_revision,
-                   apr_pool_t *result_pool,
-                   void **child_baton)
-{
-  apr_pool_t *scratch_pool = result_pool;
-  struct ev3_dir_baton *pb = parent_baton;
-  struct ev3_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, relpath);
-  db->base_revision = base_revision;
-  if (! SVN_IS_VALID_REVNUM(base_revision))
-    SVN_DBG(("ev3_open_directory('%s', base_revision == -1)  ### need to resolve to HEAD?", path));
-
-  if (pb->copyfrom_relpath)
-    {
-      /* We are inside a copy. */
-      const char *name = svn_relpath_basename(relpath, scratch_pool);
-
-      db->copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath, name,
-                                              result_pool);
-      db->copyfrom_rev = pb->copyfrom_rev;
-    }
-
-  *child_baton = db;
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_change_dir_prop(void *dir_baton,
-                    const char *name,
-                    const svn_string_t *value,
-                    apr_pool_t *scratch_pool)
-{
-  struct ev3_dir_baton *db = dir_baton;
-
-  SVN_ERR(apply_propedit(db->eb, db->path, svn_node_dir,
-                         db->base_revision, db->copyfrom_relpath, db->copyfrom_rev,
-                         name, value, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_close_directory(void *dir_baton,
-                    apr_pool_t *scratch_pool)
-{
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_add_file(const char *path,
-             void *parent_baton,
-             const char *copyfrom_path,
-             svn_revnum_t copyfrom_revision,
-             apr_pool_t *result_pool,
-             void **file_baton)
-{
-  apr_pool_t *scratch_pool = result_pool;
-  struct ev3_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
-  struct ev3_dir_baton *pb = parent_baton;
-  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
-  change_node_t *change;
-
-  SVN_ERR(insert_change_ev1_rules(&change, pb->eb->changes, relpath,
-                                  RESTRUCTURE_ADD, svn_node_file));
-
-  /* Record the observed order. */
-  APR_ARRAY_PUSH(pb->eb->path_order, const char *)
-    = apr_pstrdup(pb->eb->edit_pool, relpath);
-
-  fb->eb = pb->eb;
-  fb->path = apr_pstrdup(result_pool, relpath);
-  fb->base_revision = SVN_INVALID_REVNUM;
-  *file_baton = fb;
-
-  /* Fetch the base text as a file FB->delta_base, if it's a copy */
-  if (copyfrom_path)
-    {
-      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_func(NULL, NULL, &fb->delta_base_text, NULL,
-                                 fb->eb->fetch_baton,
-                                 change->copyfrom_path,
-                                 change->copyfrom_rev,
-                                 result_pool, scratch_pool));
-      fb->copyfrom_relpath = change->copyfrom_path;
-      fb->copyfrom_rev = change->copyfrom_rev;
-    }
-  else
-    {
-      /* It's a plain add -- we don't have a base. */
-      fb->delta_base_text = NULL;
-
-      if (pb->copyfrom_relpath)
-        {
-          const char *name = svn_relpath_basename(relpath, scratch_pool);
-          fb->copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath, name,
-                                                  result_pool);
-          fb->copyfrom_rev = pb->copyfrom_rev;
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_open_file(const char *path,
-              void *parent_baton,
-              svn_revnum_t base_revision,
-              apr_pool_t *result_pool,
-              void **file_baton)
-{
-  apr_pool_t *scratch_pool = result_pool;
-  struct ev3_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
-  struct ev3_dir_baton *pb = parent_baton;
-  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
-
-  SVN_DBG(("ev3_open_file(%s@%ld)", path, base_revision));
-
-  fb->eb = pb->eb;
-  fb->path = apr_pstrdup(result_pool, relpath);
-  fb->base_revision = base_revision;
-  if (! SVN_IS_VALID_REVNUM(base_revision))
-    SVN_DBG(("ev3_open_file(%s@%ld): base_revision == -1; should we resolve to head?",
-             path, base_revision));
-
-  if (pb->copyfrom_relpath)
-    {
-      /* We're in a copied directory, so delta is based on copy source. */
-      const char *name = svn_relpath_basename(relpath, scratch_pool);
-      const char *copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath,
-                                                      name, scratch_pool);
-
-      fb->copyfrom_relpath = copyfrom_relpath;
-      fb->copyfrom_rev = pb->copyfrom_rev;
-      SVN_ERR(fb->eb->fetch_func(NULL, NULL, &fb->delta_base_text, NULL,
-                                 fb->eb->fetch_baton,
-                                 copyfrom_relpath, pb->copyfrom_rev,
-                                 result_pool, scratch_pool));
-    }
-  else
-    {
-      SVN_ERR(fb->eb->fetch_func(NULL, NULL, &fb->delta_base_text, NULL,
-                                 fb->eb->fetch_baton,
-                                 relpath, base_revision,
-                                 result_pool, scratch_pool));
-    }
-
-  *file_baton = fb;
-  return SVN_NO_ERROR;
-}
-
-struct ev3_handler_baton
-{
-  svn_txdelta_window_handler_t apply_handler;
-  void *apply_baton;
-
-  svn_stream_t *source;
-
-  apr_pool_t *pool;
-};
-
-static svn_error_t *
-ev3_window_handler(svn_txdelta_window_t *window, void *baton)
-{
-  struct ev3_handler_baton *hb = baton;
-  svn_error_t *err;
-
-  /*
-  if (! window)
-    SVN_DBG(("ev3 window handler(%s): END",
-             hb->fb->path));
-  else if (window->new_data)
-    SVN_DBG(("ev3 window handler(%s): with new data [%d]'%s'",
-             hb->fb->path, (int)window->new_data->len, window->new_data->data));
-  else
-    SVN_DBG(("ev3 window handler(%s): with no new data",
-             hb->fb->path));
-  */
-  err = hb->apply_handler(window, hb->apply_baton);
-  if (window != NULL && !err)
-    return SVN_NO_ERROR;
-
-  SVN_ERR(svn_stream_close(hb->source));
-
-  svn_pool_destroy(hb->pool);
-
-  return svn_error_trace(err);
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_apply_textdelta(void *file_baton,
-                    const char *base_checksum,
-                    apr_pool_t *result_pool,
-                    svn_txdelta_window_handler_t *handler,
-                    void **handler_baton)
-{
-  struct ev3_file_baton *fb = file_baton;
-  apr_pool_t *handler_pool = svn_pool_create(fb->eb->edit_pool);
-  struct ev3_handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
-  change_node_t *change;
-  svn_stream_t *target;
-
-  SVN_ERR(insert_change_ev1_rules(&change, fb->eb->changes, fb->path,
-                                  RESTRUCTURE_NONE, svn_node_file));
-
-  /* Record the observed order. */
-  APR_ARRAY_PUSH(fb->eb->path_order, const char *)
-    = apr_pstrdup(fb->eb->edit_pool, fb->path);
-
-  /* The content for this path must be changed only once. (Not explicitly
-     mandated by svn_delta_editor, but we'll assume it is mandatory.) */
-  VERIFY(!change->contents_changed);
-  change->contents_changed = TRUE;
-
-  /* This can come after property changes or no changes or an add. */
-  VERIFY(!SVN_IS_VALID_REVNUM(change->changing_rev)
-         || change->changing_rev == fb->base_revision);
-  change->changing_rev = fb->base_revision;
-
-  if (! fb->delta_base_text)
-    {
-      /*SVN_DBG(("apply_textdelta(%s): preparing read from delta-base <empty stream>",
-               fb->path));*/
-      hb->source = svn_stream_empty(handler_pool);
-    }
-  else
-    {
-      /*SVN_DBG(("apply_textdelta(%s): preparing read of delta-base '%.20s...'",
-               fb->path, fb->delta_base_text));*/
-      hb->source = svn_stream_from_stringbuf(fb->delta_base_text, handler_pool);
-    }
-
-  change->contents_text = svn_stringbuf_create_empty(fb->eb->edit_pool);
-  target = svn_stream_from_stringbuf(change->contents_text, fb->eb->edit_pool);
-
-  svn_txdelta_apply(hb->source, target,
-                    NULL, NULL,
-                    handler_pool,
-                    &hb->apply_handler, &hb->apply_baton);
-
-  hb->pool = handler_pool;
-
-  *handler_baton = hb;
-  *handler = ev3_window_handler;
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_change_file_prop(void *file_baton,
-                     const char *name,
-                     const svn_string_t *value,
-                     apr_pool_t *scratch_pool)
-{
-  struct ev3_file_baton *fb = file_baton;
-
-#ifdef SHIM_WITH_UNLOCK
-  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. */
-
-      /* ### this duplicates much of apply_propedit(). fix in future.  */
-      const char *relpath = map_to_repos_relpath(fb->eb, fb->path,
-                                                 scratch_pool);
-      change_node_t *change = locate_change(fb->eb, relpath);
-
-      change->unlock = TRUE;
-    }
-#endif
-
-  SVN_ERR(apply_propedit(fb->eb, fb->path, svn_node_file,
-                         fb->base_revision, fb->copyfrom_relpath, fb->copyfrom_rev,
-                         name, value, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_close_file(void *file_baton,
-               const char *text_checksum,
-               apr_pool_t *scratch_pool)
-{
-  struct ev3_file_baton *fb = file_baton;
-  change_node_t *change = svn_hash_gets(fb->eb->changes, fb->path);
-
-  /* If this file is being modified, or copied, and apply_txdelta()
-     was not called (i.e. CONTENTS_CHANGED is FALSE), then there is
-     no change to the content. We must fetch the original content
-     in order to tell Ev3 not to change it.
-     (### Or we could retract the changing of this file entirely
-     if there were no prop changes either.)
-
-     The only exception is for a new, empty file, where we leave
-     CONTENTS_CHANGED false for now (and CONTENTS_ABSPATH null), and
-     generate an empty stream for it later. */
-  /*SVN_DBG(("close_file(%s): action=%d, contents_changed=%d, contents_abspath='%s'",
-           fb->path, change->action, change->contents_changed, change->contents_abspath));*/
-  if (! change->contents_changed
-      && (change->action == RESTRUCTURE_NONE || change->copyfrom_path))
-    {
-      change->contents_changed = TRUE;
-      change->contents_text = svn_stringbuf_dup(fb->delta_base_text,
-                                                fb->eb->edit_pool);
-      SVN_DBG(("close_file(%s): unchanged => use base text '%.20s...'",
-               fb->path, change->contents_text->data));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_absent_directory(const char *path,
-                     void *parent_baton,
-                     apr_pool_t *scratch_pool)
-{
-#ifdef SHIM_WITH_ADD_ABSENT
-  struct ev3_dir_baton *pb = parent_baton;
-  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
-  change_node_t *change;
-
-  SVN_ERR(insert_change_ev1_rules(&change, eb->changes, relpath,
-                                  RESTRUCTURE_ADD_ABSENT, svn_node_dir));
-
-  /* Record the observed order. */
-  APR_ARRAY_PUSH(pb->eb->path_order, const char *)
-    = apr_pstrdup(pb->eb->edit_pool, path);
-#endif
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_absent_file(const char *path,
-                void *parent_baton,
-                apr_pool_t *scratch_pool)
-{
-#ifdef SHIM_WITH_ADD_ABSENT
-  struct ev3_dir_baton *pb = parent_baton;
-  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
-  change_node_t *change;
-
-  SVN_ERR(insert_change_ev1_rules(&change, eb->changes, relpath,
-                                  RESTRUCTURE_ADD_ABSENT, svn_node_file));
-
-  /* Record the observed order. */
-  APR_ARRAY_PUSH(pb->eb->path_order, const char *)
-    = apr_pstrdup(pb->eb->edit_pool, path);
-#endif
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_close_edit(void *edit_baton,
-               apr_pool_t *scratch_pool)
-{
-  struct ev3_edit_baton *eb = edit_baton;
-
-  SVN_ERR(run_actions(edit_baton, scratch_pool));
-  eb->closed = TRUE;
-  SVN_ERR(svn_editor3_complete(eb->editor));
-  return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t method. */
-static svn_error_t *
-ev3_abort_edit(void *edit_baton,
-               apr_pool_t *scratch_pool)
-{
-  struct ev3_edit_baton *eb = edit_baton;
-
-#ifdef SHIM_WITH_ACTIONS_DURING_ABORT
-  SVN_ERR(run_actions(edit_baton, scratch_pool));
-#endif
-
-  if (!eb->closed)
-    SVN_ERR(svn_editor3_abort(eb->editor));
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_delta__delta_from_ev3_for_commit(
-                        const svn_delta_editor_t **deditor,
-                        void **dedit_baton,
-                        svn_editor3_t *editor,
-                        const char *repos_root_url,
-                        const char *base_relpath,
-                        svn_editor3__shim_fetch_func_t fetch_func,
-                        void *fetch_baton,
-                        const svn_editor3__shim_connector_t *shim_connector,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
-{
-  static const svn_delta_editor_t delta_editor = {
-    ev3_set_target_revision,
-    ev3_open_root,
-    ev3_delete_entry,
-    ev3_add_directory,
-    ev3_open_directory,
-    ev3_change_dir_prop,
-    ev3_close_directory,
-    ev3_absent_directory,
-    ev3_add_file,
-    ev3_open_file,
-    ev3_apply_textdelta,
-    ev3_change_file_prop,
-    ev3_close_file,
-    ev3_absent_file,
-    ev3_close_edit,
-    ev3_abort_edit
-  };
-  struct ev3_edit_baton *eb = apr_pcalloc(result_pool, sizeof(*eb));
-
-  if (!base_relpath)
-    base_relpath = "";
-  else if (base_relpath[0] == '/')
-    base_relpath += 1;
-
-  eb->editor = editor;
-  eb->changes = apr_hash_make(result_pool);
-  eb->path_order = apr_array_make(result_pool, 1, sizeof(const char *));
-  eb->edit_pool = result_pool;
-  eb->shim_connector = shim_connector;
-  if (shim_connector)
-    {
-#ifdef SHIM_WITH_ABS_PATHS
-      *eb->shim_connector->ev1_absolute_paths = FALSE;
-#endif
-    }
-  eb->repos_root_url = apr_pstrdup(result_pool, repos_root_url);
-  eb->base_relpath = apr_pstrdup(result_pool, base_relpath);
-
-  eb->fetch_func = fetch_func;
-  eb->fetch_baton = fetch_baton;
-
-  *dedit_baton = eb;
-  *deditor = &delta_editor;
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_delta__delta_from_ev3_for_update(
-                        const svn_delta_editor_t **deditor,
-                        void **dedit_baton,
-                        svn_update_editor3_t *update_editor,
-                        const char *repos_root_url,
-                        const char *base_repos_relpath,
-                        svn_editor3__shim_fetch_func_t fetch_func,
-                        void *fetch_baton,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
-{
-  svn_editor3__shim_connector_t *shim_connector
-    = apr_pcalloc(result_pool, sizeof(*shim_connector));
-
-  shim_connector->target_revision_func = update_editor->set_target_revision_func;
-  shim_connector->baton = update_editor->set_target_revision_baton;
-#ifdef SHIM_WITH_ABS_PATHS
-  shim_connector->ev1_absolute_paths /*...*/;
-#endif
-
-  SVN_ERR(svn_delta__delta_from_ev3_for_commit(
-                        deditor, dedit_baton,
-                        update_editor->editor,
-                        repos_root_url, base_repos_relpath,
-                        fetch_func, fetch_baton,
-                        shim_connector,
-                        result_pool, scratch_pool));
-  SVN_ERR(svn_delta__get_debug_editor(deditor, dedit_baton,
-                                      *deditor, *dedit_baton,
-                                      "[UP>1] ", result_pool));
-
-  return SVN_NO_ERROR;
-}
-
-
-/*
- * ===================================================================
- * Commit Editor converter to join a v3 driver to a v1 consumer
- * ===================================================================
- *
- * This editor buffers all the changes before driving the Ev1 at the end,
- * since it needs to do a single depth-first traversal of the path space
- * and this cannot be started until all moves are known.
- *
- * Moves are converted to copy-and-delete, with the copy being from
- * the source peg rev. (### Should it request copy-from revision "-1"?)
- *
- * It works like this:
- *
- *                +------+--------+
- *                | path | change |
- *      Ev3  -->  +------+--------+  -->  Ev1
- *                | ...  | ...    |
- *                | ...  | ...    |
- *
- *   1. Ev3 changes are accumulated in a per-path table, EB->changes.
- *
- *   2. On Ev3 close-edit, walk through the table in a depth-first order,
- *      sending the equivalent Ev1 action for each change.
- *
- * TODO
- *
- * ### For changes inside a copied subtree, the calls to the "open dir"
- *     and "open file" Ev1 methods may be passing the wrong revision
- *     number: see comment in apply_change().
- *
- * ### Have we got our rel-paths in order? Ev1, Ev3 and callbacks may
- *     all expect different paths. 'repos_relpath' or relative to
- *     eb->base_relpath? Leading slash (unimplemented 'send_abs_paths'
- *     feature), etc.
- *
- * ### May be tidier for OPEN_ROOT_FUNC callback (see open_root_ev3())
- *     not to actually open the root in advance, but instead just to
- *     remember the base revision that the driver wants us to specify
- *     when we do open it.
- */
-
-
-/* Record a move of a subtree from INITIAL_RELPATH to CURRENT_RELPATH.
- */
-static void
-record_move(apr_hash_t *moves,
-            const char *initial_relpath,
-            const char *current_relpath)
-{
-  apr_pool_t *pool = apr_hash_pool_get(moves);
-
-  svn_hash_sets(moves, apr_pstrdup(pool, initial_relpath),
-                apr_pstrdup(pool, current_relpath));
-}
-
-/* Return the path to which INITIAL_RELPATH would be moved, according to
- * the information in MOVES. Return INITIAL_RELPATH unchanged if it would
- * not be moved.
- */
-static const char *
-find_move(apr_hash_t *moves,
-          const char *initial_relpath,
-          apr_pool_t *scratch_pool)
-{
-  const char *p = initial_relpath;
-  apr_hash_index_t *hi;
-
-  /* follow moves: find the longest matching prefix */
-  for (hi = apr_hash_first(scratch_pool, moves);
-       hi; hi = apr_hash_next(hi))
-    {
-      const char *this_from_relpath = apr_hash_this_key(hi);
-      const char *this_to_relpath = apr_hash_this_val(hi);
-      const char *r
-        = svn_relpath_skip_ancestor(this_from_relpath, initial_relpath);
-
-      if (r)
-        {
-          p = svn_relpath_join(this_to_relpath, r, scratch_pool);
-          /* look for a longer match unless we found an exact match */
-          if (r[0] == '\0')
-            break;
-        }
-    }
-
-  return p;
-}
-
-
-/*
- * ========================================================================
- * Element-Based Branching
- * ========================================================================
- */
-
-svn_branch_repos_t *
-svn_branch_repos_create(apr_pool_t *result_pool)
-{
-  svn_branch_repos_t *repos = apr_pcalloc(result_pool, sizeof(*repos));
-
-  repos->rev_roots = apr_array_make(result_pool, 1, sizeof(void *));
-  repos->families = apr_hash_make(result_pool);
-  repos->pool = result_pool;
-  return repos;
-}
-
-/* Find the existing family with id FID in REPOS.
- *
- * Return NULL if not found.
- *
- * Note: An FID is unique among all families.
- */
-static svn_branch_family_t *
-repos_get_family_by_id(svn_branch_repos_t *repos,
-                       int fid)
-{
-  return apr_hash_get(repos->families, &fid, sizeof(fid));
-}
-
-static void
-repos_register_family(svn_branch_repos_t *repos,
-                      svn_branch_family_t *family)
-{
-  int fid = family->fid;
-
-  apr_hash_set(repos->families,
-               apr_pmemdup(repos->pool, &fid, sizeof(fid)), sizeof(fid),
-               family);
-}
-
-svn_branch_revision_root_t *
-svn_branch_revision_root_create(svn_branch_repos_t *repos,
-                                svn_revnum_t rev,
-                                struct svn_branch_instance_t *root_branch,
-                                apr_pool_t *result_pool)
-{
-  svn_branch_revision_root_t *rev_root
-    = apr_pcalloc(result_pool, sizeof(*rev_root));
-
-  rev_root->repos = repos;
-  rev_root->rev = rev;
-  rev_root->root_branch = root_branch;
-  rev_root->branch_instances = apr_array_make(result_pool, 1, sizeof(void *));
-  return rev_root;
-}
-
-svn_branch_family_t *
-svn_branch_family_create(svn_branch_repos_t *repos,
-                         int fid,
-                         int first_bid,
-                         int next_bid,
-                         int first_eid,
-                         int next_eid,
-                         apr_pool_t *result_pool)
-{
-  svn_branch_family_t *f = apr_pcalloc(result_pool, sizeof(*f));
-
-  f->fid = fid;
-  f->repos = repos;
-  f->branch_siblings = apr_array_make(result_pool, 1, sizeof(void *));
-  f->sub_families = apr_array_make(result_pool, 1, sizeof(void *));
-  f->first_bid = first_bid;
-  f->next_bid = next_bid;
-  f->first_eid = first_eid;
-  f->next_eid = next_eid;
-  f->pool = result_pool;
-  return f;
-}
-
-/* Assign a new element id in FAMILY.
- */
-static int
-family_add_new_element(svn_branch_family_t *family)
-{
-  int eid = family->next_eid++;
-
-  return eid;
-}
-
-/* Create a new, empty family in OUTER_FAMILY.
- */
-static svn_branch_family_t *
-family_add_new_subfamily(svn_branch_family_t *outer_family)
-{
-  svn_branch_repos_t *repos = outer_family->repos;
-  int fid = repos->next_fid++;
-  svn_branch_family_t *family
-    = svn_branch_family_create(repos, fid,
-                               fid * 10, fid * 10,
-                               fid * 100, fid * 100,
-                               outer_family->pool);
-
-  /* Register the family */
-  repos_register_family(repos, family);
-  APR_ARRAY_PUSH(outer_family->sub_families, void *) = family;
-
-  return family;
-}
-
-/* Create a new branch sibling in FAMILY, with branch id BID and
- * root element ROOT_EID, and register it as a member of the family.
- */
-static svn_branch_sibling_t *
-family_create_branch_sibling(svn_branch_family_t *family,
-                             int bid,
-                             int root_eid)
-{
-  svn_branch_sibling_t *branch_sibling
-    = svn_branch_sibling_create(family, bid, root_eid, family->pool);
-
-  /* The root EID must be an existing EID. */
-  SVN_ERR_ASSERT_NO_RETURN(root_eid >= family->first_eid
-                           /*&& root_eid < family->next_eid*/);
-  /* ROOT_RRPATH must not be within another branch of the family. */
-
-  /* Register the branch */
-  APR_ARRAY_PUSH(family->branch_siblings, void *) = branch_sibling;
-
-  return branch_sibling;
-}
-
-/* Return the branch sibling definition with branch id BID in FAMILY.
- *
- * Return NULL if not found.
- */
-static svn_branch_sibling_t *
-family_find_branch_sibling(svn_branch_family_t *family,
-                           int bid)
-{
-  int i;
-
-  for (i = 0; i < family->branch_siblings->nelts; i++)
-    {
-      svn_branch_sibling_t *this
-        = APR_ARRAY_IDX(family->branch_siblings, i, void *);
-
-      if (this->bid == bid)
-        return this;
-    }
-  return NULL;
-}
-
-/* Return an existing (if found) or new (otherwise) branch sibling
- * definition object with id BID and root-eid ROOT_EID in FAMILY.
- */
-static svn_branch_sibling_t *
-family_find_or_create_branch_sibling(svn_branch_family_t *family,
-                                     int bid,
-                                     int root_eid)
-{
-  svn_branch_sibling_t *sibling = family_find_branch_sibling(family, bid);
-
-  if (!sibling)
-    {
-      sibling = family_create_branch_sibling(family, bid, root_eid);
-    }
-
-  SVN_ERR_ASSERT_NO_RETURN(sibling->root_eid == root_eid);
-  return sibling;
-}
-
-/* Add a new branch sibling definition to FAMILY, with root element id
- * ROOT_EID.
- */
-static svn_branch_sibling_t *
-family_add_new_branch_sibling(svn_branch_family_t *family,
-                              int root_eid)
-{
-  int bid = family->next_bid++;
-  svn_branch_sibling_t *branch_sibling
-    = family_create_branch_sibling(family, bid, root_eid);
-
-  return branch_sibling;
-}
-
-apr_array_header_t *
-svn_branch_family_get_children(svn_branch_family_t *family,
-                               apr_pool_t *result_pool)
-{
-  return family->sub_families;
-}
-
-apr_array_header_t *
-svn_branch_family_get_branch_instances(
-                                svn_branch_revision_root_t *rev_root,
-                                svn_branch_family_t *family,
-                                apr_pool_t *result_pool)
-{
-  apr_array_header_t *rev_branch_instances = rev_root->branch_instances;
-  apr_array_header_t *fam_branch_instances
-    = apr_array_make(result_pool, 0, sizeof(void *));
-  int i;
-
-  for (i = 0; i < rev_branch_instances->nelts; i++)
-    {
-      svn_branch_instance_t *branch
-        = APR_ARRAY_IDX(rev_branch_instances, i, svn_branch_instance_t *);
-
-      if (branch->sibling_defn->family == family)
-        APR_ARRAY_PUSH(fam_branch_instances, void *) = branch;
-    }
-
-  return fam_branch_instances;
-}
-
-svn_branch_sibling_t *
-svn_branch_sibling_create(svn_branch_family_t *family,
-                             int bid,
-                             int root_eid,
-                             apr_pool_t *result_pool)
-{
-  svn_branch_sibling_t *b = apr_pcalloc(result_pool, sizeof(*b));
-
-  SVN_ERR_ASSERT_NO_RETURN(bid >= family->first_bid
-                           && bid < family->next_bid);
-  SVN_ERR_ASSERT_NO_RETURN(root_eid >= family->first_eid
-                           && root_eid < family->next_eid);
-
-  b->family = family;
-  b->bid = bid;
-  b->root_eid = root_eid;
-  return b;
-}
-
-svn_branch_instance_t *
-svn_branch_instance_create(svn_branch_sibling_t *branch_sibling,
-                           svn_branch_revision_root_t *rev_root,
-                           const char *branch_root_rrpath,
-                           apr_pool_t *result_pool)
-{
-  svn_branch_instance_t *b = apr_pcalloc(result_pool, sizeof(*b));
-
-  b->sibling_defn = branch_sibling;
-  b->rev_root = rev_root;
-  b->e_map = apr_hash_make(result_pool);
-  b->branch_root_rrpath = apr_pstrdup(result_pool, branch_root_rrpath);
-  return b;
-}
-
-svn_branch_el_rev_id_t *
-svn_branch_el_rev_id_create(svn_branch_instance_t *branch,
-                            int eid,
-                            svn_revnum_t rev,
-                            apr_pool_t *result_pool)
-{
-  svn_branch_el_rev_id_t *id = apr_palloc(result_pool, sizeof(*id));
-
-  id->branch = branch;
-  id->eid = eid;
-  id->rev = rev;
-  return id;
-}
-
-svn_branch_el_rev_content_t *
-svn_branch_el_rev_content_create(svn_editor3_eid_t parent_eid,
-                                 const char *name,
-                                 const svn_editor3_node_content_t *node_content,
-                                 apr_pool_t *result_pool)
-{
-  svn_branch_el_rev_content_t *content
-     = apr_palloc(result_pool, sizeof(*content));
-
-  content->parent_eid = parent_eid;
-  content->name = apr_pstrdup(result_pool, name);
-  content->content = svn_editor3_node_content_dup(node_content, result_pool);
-  return content;
-}
-
-svn_branch_el_rev_content_t *
-svn_branch_el_rev_content_dup(const svn_branch_el_rev_content_t *old,
-                              apr_pool_t *result_pool)
-{
-  svn_branch_el_rev_content_t *content
-     = apr_pmemdup(result_pool, old, sizeof(*content));
-
-  content->name = apr_pstrdup(result_pool, old->name);
-  content->content = svn_editor3_node_content_dup(old->content, result_pool);
-  return content;
-}
-
-svn_boolean_t
-svn_branch_el_rev_content_equal(const svn_branch_el_rev_content_t *content_left,
-                                const svn_branch_el_rev_content_t *content_right,
-                                apr_pool_t *scratch_pool)
-{
-  if (!content_left && !content_right)
-    {
-      return TRUE;
-    }
-  else if (!content_left || !content_right)
-    {
-      return FALSE;
-    }
-
-  if (content_left->parent_eid != content_right->parent_eid)
-    {
-      return FALSE;
-    }
-  if (strcmp(content_left->name, content_right->name) != 0)
-    {
-      return FALSE;
-    }
-  if (! svn_editor3_node_content_equal(content_left->content,
-                                       content_right->content,
-                                       scratch_pool))
-    {
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-
-/*
- * ========================================================================
- * Branch mappings
- * ========================================================================
- */
-
-const char *
-svn_branch_get_root_rrpath(const svn_branch_instance_t *branch)
-{
-  const char *root_rrpath = branch->branch_root_rrpath;
-
-  SVN_ERR_ASSERT_NO_RETURN(root_rrpath);
-  return root_rrpath;
-}
-
-/* Validate that NODE is suitable for a mapping of BRANCH:EID.
- * NODE->content may be null.
- */
-static void
-branch_map_node_validate(const svn_branch_instance_t *branch,
-                         int eid,
-                         const svn_branch_el_rev_content_t *node)
-{
-  SVN_ERR_ASSERT_NO_RETURN(node);
-
-  /* Parent EID must be valid, or -1 iff EID is the branch root. */
-  SVN_ERR_ASSERT_NO_RETURN(
-    (eid == branch->sibling_defn->root_eid)
-    ? (node->parent_eid == -1)
-    : (node->parent_eid >= branch->sibling_defn->family->first_eid
-       && node->parent_eid < branch->sibling_defn->family->next_eid));
-
-  /* Node name must be given, and empty iff EID is the branch root. */
-  SVN_ERR_ASSERT_NO_RETURN(
-    node->name
-    && (eid == branch->sibling_defn->root_eid) == (*node->name == '\0'));
-
-  /* Content, if specified, must be in full or by reference. */
-  if (node->content)
-    SVN_ERR_ASSERT_NO_RETURN(node->content
-                             && ((SVN_IS_VALID_REVNUM(node->content->ref.rev)
-                                  && node->content->ref.relpath)
-                                 || (node->content->kind != svn_node_unknown
-                                     && node->content->kind != svn_node_none)));
-}
-
-/* In BRANCH, get element EID's node (parent, name, content).
- *
- * If element EID is not present, return null. Otherwise, the returned
- * node's content may be null meaning it is unknown.
- */
-static svn_branch_el_rev_content_t *
-branch_map_get(const svn_branch_instance_t *branch,
-               int eid)
-{
-  svn_branch_el_rev_content_t *node;
-
-  SVN_ERR_ASSERT_NO_RETURN(eid >= branch->sibling_defn->family->first_eid
-                           && eid < branch->sibling_defn->family->next_eid);
-
-  node = apr_hash_get(branch->e_map, &eid, sizeof(eid));
-
-  if (node)
-    branch_map_node_validate(branch, eid, node);
-  return node;
-}
-
-/* In BRANCH, set element EID's node (parent, name, content) to NODE.
- *
- * If NODE is null, delete element EID. Otherwise, NODE->content may be
- * null meaning it is unknown.
- *
- * Assume NODE is already allocated with sufficient lifetime.
- */
-static void
-branch_map_set(svn_branch_instance_t *branch,
-               int eid,
-               svn_branch_el_rev_content_t *node)
-{
-  apr_pool_t *map_pool = apr_hash_pool_get(branch->e_map);
-  int *eid_p = apr_pmemdup(map_pool, &eid, sizeof(eid));
-
-  SVN_ERR_ASSERT_NO_RETURN(eid >= branch->sibling_defn->family->first_eid
-                           && eid < branch->sibling_defn->family->next_eid);
-  if (node)
-    branch_map_node_validate(branch, eid, node);
-
-  apr_hash_set(branch->e_map, eid_p, sizeof(*eid_p), node);
-}
-
-/* In BRANCH, delete element EID.
- */
-static void
-branch_map_delete(svn_branch_instance_t *branch,
-                  int eid)
-{
-  SVN_ERR_ASSERT_NO_RETURN(eid >= branch->sibling_defn->family->first_eid
-                           && eid < branch->sibling_defn->family->next_eid);
-
-  branch_map_set(branch, eid, NULL);
-}
-
-/* Set or change the EID:element mapping for EID in BRANCH.
- *
- * Duplicate NEW_NAME and NEW_CONTENT into the branch mapping's pool.
- */
-static void
-branch_map_update(svn_branch_instance_t *branch,
-                  int eid,
-                  svn_editor3_eid_t new_parent_eid,
-                  const char *new_name,
-                  const svn_editor3_node_content_t *new_content)
-{
-  apr_pool_t *map_pool = apr_hash_pool_get(branch->e_map);
-  svn_branch_el_rev_content_t *node
-    = svn_branch_el_rev_content_create(new_parent_eid, new_name, new_content,
-                                       map_pool);
-
-  /* EID must be a valid element id of the branch family */
-  SVN_ERR_ASSERT_NO_RETURN(eid >= branch->sibling_defn->family->first_eid
-                           && eid < branch->sibling_defn->family->next_eid);
-  /* NEW_CONTENT must be specified, either in full or by reference */
-  SVN_ERR_ASSERT_NO_RETURN(new_content);
-
-  /* We don't expect to be called more than once per eid. */
-  /*SVN_ERR_ASSERT_NO_RETURN(branch_map_get(branch, eid) == NULL); ### hmm, no! */
-
-  /* Insert the new version */
-  branch_map_set(branch, eid, node);
-}
-
-/* Set or change the EID:element mapping for EID in BRANCH to reflect a
- * subbranch root node. This node has no content in this branch; the
- * corresponding element of the subbranch will define its content.
- *
- * Duplicate NEW_NAME into the branch mapping's pool.
- */
-static void
-branch_map_update_as_subbranch_root(svn_branch_instance_t *branch,
-                                    int eid,
-                                    svn_editor3_eid_t new_parent_eid,
-                                    const char *new_name)
-{
-  apr_pool_t *map_pool = apr_hash_pool_get(branch->e_map);
-  svn_branch_el_rev_content_t *node
-    = svn_branch_el_rev_content_create(new_parent_eid, new_name, NULL /*content*/,
-                                       map_pool);
-
-  /* EID must be a valid element id of the branch family */
-  SVN_ERR_ASSERT_NO_RETURN(eid >= branch->sibling_defn->family->first_eid
-                           && eid < branch->sibling_defn->family->next_eid);
-  branch_map_node_validate(branch, eid, node);
-
-  /* We don't expect to be called more than once per eid. */
-  /*SVN_ERR_ASSERT_NO_RETURN(branch_map_get(branch, eid) == NULL); ### hmm, no! */
-
-  /* Insert the new version */
-  branch_map_set(branch, eid, node);
-}
-
-/* Remove from BRANCH's mapping any elements that do not have a complete
- * line of parents to the branch root. In other words, remove elements
- * that have been implicitly deleted.
- *
- * This does not remove subbranches.
- */
-static void
-branch_map_purge_orphans(svn_branch_instance_t *branch,
-                         apr_pool_t *scratch_pool)
-{
-  apr_hash_index_t *hi;
-  svn_boolean_t changed;
-
-  do
-    {
-      changed = FALSE;
-
-      for (hi = apr_hash_first(scratch_pool, branch->e_map);
-           hi; hi = apr_hash_next(hi))
-        {
-          int this_eid = *(const int *)apr_hash_this_key(hi);
-          svn_branch_el_rev_content_t *this_node = apr_hash_this_val(hi);
-
-          if (this_node->parent_eid != -1
-              && ! branch_map_get(branch, this_node->parent_eid))
-            {
-              SVN_DBG(("purge orphan: e%d", this_eid));
-              branch_map_delete(branch, this_eid);
-              changed = TRUE;
-            }
-        }
-    }
-  while (changed);
-}
-
-const char *
-svn_branch_get_path_by_eid(const svn_branch_instance_t *branch,
-                           int eid,
-                           apr_pool_t *result_pool)
-{
-  const char *path = "";
-  svn_branch_el_rev_content_t *node;
-
-  SVN_ERR_ASSERT_NO_RETURN(eid >= branch->sibling_defn->family->first_eid
-                           && eid < branch->sibling_defn->family->next_eid);
-
-  for (; eid != branch->sibling_defn->root_eid; eid = node->parent_eid)
-    {
-      node = branch_map_get(branch, eid);
-      if (! node)
-        return NULL;
-      path = svn_relpath_join(node->name, path, result_pool);
-    }
-  SVN_ERR_ASSERT_NO_RETURN(eid == branch->sibling_defn->root_eid);
-  return path;
-}
-
-const char *
-svn_branch_get_rrpath_by_eid(const svn_branch_instance_t *branch,
-                             int eid,
-                             apr_pool_t *result_pool)
-{
-  const char *path = svn_branch_get_path_by_eid(branch, eid, result_pool);
-  const char *rrpath = NULL;
-
-  if (path)
-    {
-      rrpath = svn_relpath_join(svn_branch_get_root_rrpath(branch),
-                                path, result_pool);
-    }
-  return rrpath;
-}
-
-/* Assuming the mapping has complete paths from the root to each element,
- * get the EID for path PATH which is relative to the root of the branch.
- */
-static int
-branch_map_get_eid_by_path(const svn_branch_instance_t *branch,
-                           const char *path,
-                           apr_pool_t *scratch_pool)
-{
-  apr_hash_index_t *hi;
-
-  /* ### This is a crude, linear search */
-  for (hi = apr_hash_first(scratch_pool, branch->e_map);
-       hi; hi = apr_hash_next(hi))
-    {
-      int eid = *(const int *)apr_hash_this_key(hi);
-      const char *this_path = svn_branch_get_path_by_eid(branch, eid,
-                                                         scratch_pool);
-
-      if (! this_path)
-        {
-          /* Mapping is not complete; this element is in effect not present. */
-          continue;
-        }
-      if (strcmp(path, this_path) == 0)
-        {
-          return eid;
-        }
-    }
-
-  return -1;
-}
-
-/* Assuming the mapping has complete paths from the root to each element,
- * get the EID for path PATH which is relative to the root of the branch.
- */
-static int
-branch_map_get_eid_by_rrpath(svn_branch_instance_t *branch,
-                             const char *rrpath,
-                             apr_pool_t *scratch_pool)
-{
-  const char *path = svn_relpath_skip_ancestor(svn_branch_get_root_rrpath(branch),
-                                               rrpath);
-  int eid = -1;
-
-  if (path)
-    {
-      eid = branch_map_get_eid_by_path(branch, path, scratch_pool);
-    }
-  return eid;
-}
-
-/* Get an element's content (props, text, ...) in full or by reference.
- */
-static svn_error_t *
-copy_content_from(svn_editor3_node_content_t **content_p,
-                  svn_branch_instance_t *from_branch,
-                  int from_eid,
-                  apr_pool_t *result_pool,
-                  apr_pool_t *scratch_pool)
-{
-  svn_branch_el_rev_content_t *old_el = branch_map_get(from_branch, from_eid);
-  svn_editor3_node_content_t *content = old_el->content;
-
-  /* If content is unknown, then presumably this is a committed rev and
-     so we can provide a reference to the committed content. */
-  if (! content)
-    {
-      svn_editor3_peg_path_t peg;
-
-      SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(from_branch->rev_root->rev));
-      peg.rev = from_branch->rev_root->rev;
-      peg.relpath = svn_branch_get_rrpath_by_eid(from_branch, from_eid,
-                                                 scratch_pool);
-      content = svn_editor3_node_content_create_ref(peg, result_pool);
-    }
-  *content_p = content;
-  return SVN_NO_ERROR;
-}
-
-/* In BRANCH, update the path mappings to delete all children of EID,
- * recursively (but not deleting subbranches).
- *
- * EID may be any element id in BRANCH: existing or nonexistent, root or
- * normal or subbranch root (useful if we have just branchified a subtree).
- */
-static svn_error_t *
-branch_map_delete_children(svn_branch_instance_t *branch,
-                           int eid,
-                           apr_pool_t *scratch_pool)
-{
-  apr_hash_index_t *hi;
-
-  for (hi = apr_hash_first(scratch_pool, branch->e_map);
-       hi; hi = apr_hash_next(hi))
-    {
-      int this_eid = *(const int *)apr_hash_this_key(hi);
-      svn_branch_el_rev_content_t *this_node = apr_hash_this_val(hi);
-
-      if (this_node->parent_eid == eid)
-        {
-          /* Recurse. (We don't try to check whether it's a directory node,
-             as we might not have the node kind in the map.) */
-          SVN_ERR(branch_map_delete_children(branch, this_eid,
-                                             scratch_pool));
-
-          /* Delete this immediate child. */
-          branch_map_delete(branch, this_eid);
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-/* In TO_BRANCH, assign new EIDs and path mappings to reflect the copying
- * of all children of FROM_BRANCH:FROM_PARENT_EID to TO_BRANCH:TO_PARENT_EID,
- * recursively, excluding the specified parent element itself.
- *
- * Assign a new EID in TO_BRANCH's family for each copied element.
- *
- * FROM_BRANCH and TO_BRANCH may be the same or different branch instances
- * in the same or different branch families.
- *
- * FROM_PARENT_EID MUST be an existing element in FROM_BRANCH. It may be the
- * root element of FROM_BRANCH, but not a subtree root.
- *
- * TO_PARENT_EID MUST be an existing path in TO_BRANCH. It may be the
- * root element of TO_BRANCH, but not a subtree root.
- */
-static svn_error_t *
-branch_map_copy_children(svn_branch_instance_t *from_branch,
-                         int from_parent_eid,
-                         svn_branch_instance_t *to_branch,
-                         int to_parent_eid,
-                         apr_pool_t *scratch_pool)
-{
-  apr_hash_index_t *hi;
-
-  /* The 'from' and 'to' nodes must exist. */
-  SVN_ERR_ASSERT(branch_map_get(from_branch, from_parent_eid));
-  SVN_ERR_ASSERT(branch_map_get(to_branch, to_parent_eid));
-
-  /* Process the immediate children of FROM_PARENT_EID. */
-  for (hi = apr_hash_first(scratch_pool, from_branch->e_map);
-       hi; hi = apr_hash_next(hi))
-    {
-      int this_from_eid = *(const int *)apr_hash_this_key(hi);
-      svn_branch_el_rev_content_t *from_node = apr_hash_this_val(hi);
-
-      if (from_node->parent_eid == from_parent_eid)
-        {
-          int new_eid = family_add_new_element(to_branch->sibling_defn->family);
-
-          branch_map_update(to_branch, new_eid,
-                            to_parent_eid, from_node->name,
-                            from_node->content);
-
-          /* Recurse. (We don't try to check whether it's a directory node,
-             as we might not have the node kind in the map.) */
-          SVN_ERR(branch_map_copy_children(from_branch, this_from_eid,
-                                           to_branch, new_eid,
-                                           scratch_pool));
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Update the mappings to reflect branching the subtree [### the what?!]
- * at FROM_BRANCH:FROM_PARENT_EID to TO_BRANCH:TO_PARENT_EID.
- *
- * FROM_BRANCH and TO_BRANCH must be different branch instances in the
- * same branch family.
- *
- * FROM_PATH MUST be an existing path in FROM_BRANCH, and may be the
- * root path of FROM_BRANCH.
- *
- * TO_PATH MUST be a path in TO_BRANCH at which nothing currently exists
- * if INCLUDE_SELF, or an existing path if not INCLUDE_SELF.
- *
- * If INCLUDE_SELF is true, include the element at FROM_PATH, otherwise
- * only act on children (recursively) of FROM_PATH.
- */
-static svn_error_t *
-branch_map_branch_children(svn_branch_instance_t *from_branch,
-                           int from_parent_eid,
-                           svn_branch_instance_t *to_branch,
-                           int to_parent_eid,
-                           apr_pool_t *scratch_pool)
-{
-  apr_hash_index_t *hi;
-
-  SVN_ERR_ASSERT(from_branch->sibling_defn->family->fid
-                 == to_branch->sibling_defn->family->fid);
-  SVN_ERR_ASSERT(from_branch->sibling_defn->bid != to_branch->sibling_defn->bid);
-
-  /* The 'from' and 'to' nodes must exist. */
-  SVN_ERR_ASSERT(branch_map_get(from_branch, from_parent_eid));
-  SVN_ERR_ASSERT(branch_map_get(to_branch, to_parent_eid));
-
-  /* Process the immediate children of FROM_PARENT_EID. */
-  for (hi = apr_hash_first(scratch_pool, from_branch->e_map);
-       hi; hi = apr_hash_next(hi))
-    {
-      int this_eid = *(const int *)apr_hash_this_key(hi);
-      svn_branch_el_rev_content_t *from_node = branch_map_get(from_branch,
-                                                              this_eid);
-
-      if (from_node->parent_eid == from_parent_eid)
-        {
-          svn_editor3_node_content_t *this_content;
-
-          SVN_ERR(copy_content_from(&this_content, from_branch, this_eid,
-                                    scratch_pool, scratch_pool));
-          branch_map_update(to_branch, this_eid,
-                            from_node->parent_eid, from_node->name,
-                            this_content);
-
-          /* Recurse. (We don't try to check whether it's a directory node,
-             as we might not have the node kind in the map.) */
-          SVN_ERR(branch_map_branch_children(from_branch, this_eid,
-                                             to_branch, this_eid,
-                                             scratch_pool));
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Return true iff CHILD_FAMILY is an immediate child of PARENT_FAMILY. */
-static svn_boolean_t
-family_is_child(svn_branch_family_t *parent_family,
-                svn_branch_family_t *child_family)
-{
-  int i;
-
-  for (i = 0; i < parent_family->sub_families->nelts; i++)
-    {
-      svn_branch_family_t *sub_family
-        = APR_ARRAY_IDX(parent_family->sub_families, i, void *);
-
-      if (sub_family == child_family)
-        return TRUE;
-    }
-  return FALSE;
-}
-
-/* Return an array of pointers to the branch instances that are immediate
- * sub-branches of BRANCH at or below EID.
- */
-static apr_array_header_t *
-branch_get_sub_branches(const svn_branch_instance_t *branch,
-                        int eid,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
-{
-  svn_branch_family_t *family = branch->sibling_defn->family;
-  const char *top_rrpath = svn_branch_get_rrpath_by_eid(branch, eid,
-                                                        scratch_pool);
-  apr_array_header_t *sub_branches = apr_array_make(result_pool, 0,
-                                                    sizeof(void *));
-  int i;
-
-  for (i = 0; i < branch->rev_root->branch_instances->nelts; i++)
-    {
-      svn_branch_instance_t *sub_branch
-        = APR_ARRAY_IDX(branch->rev_root->branch_instances, i, void *);
-      svn_branch_family_t *sub_branch_family = sub_branch->sibling_defn->family;
-      const char *sub_branch_root_rrpath
-        = svn_branch_get_root_rrpath(sub_branch);
-
-      /* Is it an immediate child at or below EID? */
-      if (family_is_child(family, sub_branch_family)
-          && svn_relpath_skip_ancestor(top_rrpath, sub_branch_root_rrpath))
-        {
-          APR_ARRAY_PUSH(sub_branches, void *) = sub_branch;
-        }
-    }
-  return sub_branches;
-}
-
-/* Return an array of pointers to the branch instances that are immediate
- * sub-branches of BRANCH.
- */
-static apr_array_header_t *
-branch_get_all_sub_branches(const svn_branch_instance_t *branch,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool)
-{
-  return branch_get_sub_branches(branch, branch->sibling_defn->root_eid,
-                                 result_pool, scratch_pool);
-}
-
-/* Delete the branch instance BRANCH by removing the record of it from its
- * revision-root.
- */
-static svn_error_t *
-branch_instance_delete(svn_branch_instance_t *branch,
-                       apr_pool_t *scratch_pool)
-{
-  apr_array_header_t *branch_instances = branch->rev_root->branch_instances;
-  int i;
-
-  for (i = 0; i < branch_instances->nelts; i++)
-    {
-      svn_branch_instance_t *b
-        = APR_ARRAY_IDX(branch_instances, i, void *);
-
-      if (b == branch)
-        {
-          svn_sort__array_delete(branch_instances, i, 1);
-          break;
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Delete the branch instance object BRANCH and any nested branch instances,
- * recursively.
- */
-static svn_error_t *
-branch_instance_delete_r(svn_branch_instance_t *branch,
-                         apr_pool_t *scratch_pool)
-{
-  apr_array_header_t *subbranches
-    = branch_get_all_sub_branches(branch, scratch_pool, scratch_pool);
-  int i;
-
-  /* Delete nested branch instances, recursively */
-  for (i = 0; i < subbranches->nelts; i++)
-    {
-      svn_branch_instance_t *b = APR_ARRAY_IDX(subbranches, i, void *);
-
-      branch_instance_delete_r(b, scratch_pool);
-    }
-
-  /* Remove the record of this branch instance */
-  SVN_ERR(branch_instance_delete(branch, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* Create a new branch instance at OUTER_BRANCH:EID, of the branch class
- * BRANCH_SIBLING, with no elements.
- */
-static svn_branch_instance_t *
-branch_add_new_branch_instance(svn_branch_instance_t *outer_branch,
-                               int outer_eid,
-                               svn_branch_sibling_t *branch_sibling,
-                               apr_pool_t *scratch_pool)
-{
-  svn_branch_family_t *family = branch_sibling->family;
-
-  /* All this next bit is to get an RRPATH. Should ultimately go away. */
-  const char *outer_root_rrpath = svn_branch_get_root_rrpath(outer_branch);
-  const char *outer_eid_relpath
-    = svn_branch_get_path_by_eid(outer_branch, outer_eid, scratch_pool);
-  const char *new_root_rrpath
-    = svn_relpath_join(outer_root_rrpath, outer_eid_relpath, scratch_pool);
-
-  svn_branch_instance_t *branch_instance
-    = svn_branch_instance_create(branch_sibling, outer_branch->rev_root,
-                                 new_root_rrpath, family->pool);
-
-  APR_ARRAY_PUSH(branch_instance->rev_root->branch_instances, void *)
-    = branch_instance;
-
-  return branch_instance;
-}
-
-
-/*
- * ========================================================================
- * Parsing and Serializing
- * ========================================================================
- */
-
-/* Create a new branch *NEW_BRANCH that belongs to FAMILY, initialized
- * with info parsed from STREAM, allocated in RESULT_POOL.
- */
-static svn_error_t *
-svn_branch_instance_parse(svn_branch_instance_t **new_branch,
-                          svn_branch_family_t *family,
-                          svn_branch_revision_root_t *rev_root,
-                          svn_stream_t *stream,
-                          apr_pool_t *result_pool,
-                          apr_pool_t *scratch_pool)
-{
-  svn_stringbuf_t *line;
-  svn_boolean_t eof;
-  int n;
-  int fid, bid, root_eid;
-  svn_branch_sibling_t *branch_sibling;
-  svn_branch_instance_t *branch_instance;
-  char branch_root_path[100];
-  const char *branch_root_rrpath;
-  int eid;
-
-  SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
-  SVN_ERR_ASSERT(! eof);
-  n = sscanf(line->data, "f%db%d: root-eid %d at %s\n",
-             &fid, &bid, &root_eid, branch_root_path);
-  SVN_ERR_ASSERT(n == 4);
-
-  SVN_ERR_ASSERT(fid == family->fid);
-  branch_root_rrpath
-    = strcmp(branch_root_path, ".") == 0 ? "" : branch_root_path;
-  branch_sibling = family_find_or_create_branch_sibling(family, bid, root_eid);
-  branch_instance = svn_branch_instance_create(branch_sibling, rev_root,
-                                               branch_root_rrpath, result_pool);
-
-  for (eid = family->first_eid; eid < family->next_eid; eid++)
-    {
-      int this_fid, this_bid, this_eid, this_parent_eid;
-      char this_name[20], this_path[100];
-
-      SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
-      SVN_ERR_ASSERT(! eof);
-      n = sscanf(line->data, "f%db%de%d: %d %20s %100s\n",
-                 &this_fid, &this_bid, &this_eid,
-                 &this_parent_eid, this_name, this_path);
-      SVN_ERR_ASSERT(n == 6);
-      if (strcmp(this_path, "(null)") != 0)
-        {
-          const char *name = strcmp(this_name, ".") == 0 ? "" : this_name;
-          const char *path = strcmp(this_path, ".") == 0 ? "" : this_path;
-          const char *rrpath = svn_relpath_join(branch_root_rrpath, path,
-                                                scratch_pool);
-          svn_editor3_peg_path_t peg;
-          svn_editor3_node_content_t *content;
-
-          /* Specify the content by reference */
-          peg.rev = rev_root->rev;
-          peg.relpath = rrpath;
-          content = svn_editor3_node_content_create_ref(peg, scratch_pool);
-
-          branch_map_update(branch_instance, this_eid,
-                            this_parent_eid, name, content);
-        }
-    }
-
-  *new_branch = branch_instance;
-  return SVN_NO_ERROR;
-}
-
-/* Parse a branch family from STREAM.
- *
- * If the family is already found in REPOS, update it (assume it's from a
- * later revision), otherwise create a new one and register it in REPOS.
- *
- * Set *NEW_FAMILY to the branch family object, allocated in REPOS's pool.
- */
-static svn_error_t *
-svn_branch_family_parse(svn_branch_family_t **new_family,
-                        int *parent_fid,
-                        svn_branch_repos_t *repos,
-                        svn_stream_t *stream,
-                        apr_pool_t *scratch_pool)
-{
-  svn_stringbuf_t *line;
-  svn_boolean_t eof;
-  int n;
-  int fid, first_bid, next_bid, first_eid, next_eid;
-  svn_branch_family_t *family;
-
-  SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
-  SVN_ERR_ASSERT(!eof);
-  n = sscanf(line->data, "f%d: bids %d %d eids %d %d parent-fid %d\n",
-             &fid,
-             &first_bid, &next_bid, &first_eid, &next_eid,
-             parent_fid);
-  SVN_ERR_ASSERT(n == 6);
-
-  family = repos_get_family_by_id(repos, fid);
-  if (family)
-    {
-      SVN_ERR_ASSERT(first_bid == family->first_bid);
-      SVN_ERR_ASSERT(next_bid >= family->next_bid);
-      SVN_ERR_ASSERT(first_eid == family->first_eid);
-      SVN_ERR_ASSERT(next_eid >= family->next_eid);
-      family->next_bid = next_bid;
-      family->next_eid = next_eid;
-    }
-  else
-    {
-      family = svn_branch_family_create(repos, fid,
-                                        first_bid, next_bid,
-                                        first_eid, next_eid,
-                                        repos->pool);
-
-      /* Register this family in the repos and in its parent family (if any) */
-      repos_register_family(repos, family);
-      if (*parent_fid >= 0)
-        {
-          svn_branch_family_t *parent_family
-            = repos_get_family_by_id(repos, *parent_fid);
-
-          SVN_ERR_ASSERT(parent_family);
-          APR_ARRAY_PUSH(parent_family->sub_families, void *) = family;
-        }
-    }
-
-  *new_family = family;
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_branch_revision_root_parse(svn_branch_revision_root_t **rev_root_p,
-                               int *next_fid_p,
-                               svn_branch_repos_t *repos,
-                               svn_stream_t *stream,
-                               apr_pool_t *result_pool,
-                               apr_pool_t *scratch_pool)
-{
-  svn_branch_revision_root_t *rev_root;
-  svn_revnum_t rev;
-  int root_fid;
-  svn_stringbuf_t *line;
-  svn_boolean_t eof;
-  int n, i;
-
-  SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
-  SVN_ERR_ASSERT(! eof);
-  n = sscanf(line->data, "r%ld: fids %*d %d root-fid %d",
-             &rev, /* 0, */ next_fid_p, &root_fid);
-  SVN_ERR_ASSERT(n == 3);
-
-  rev_root = svn_branch_revision_root_create(repos, rev, NULL /*root_branch*/,
-                                             result_pool);
-
-  /* parse the families */
-  for (i = 0; i < *next_fid_p; i++)
-    {
-      svn_branch_family_t *family;
-      int bid, parent_fid;
-
-      SVN_ERR(svn_branch_family_parse(&family, &parent_fid, repos, stream,
-                                      scratch_pool));
-
-      /* parse the branches */
-      for (bid = family->first_bid; bid < family->next_bid; ++bid)
-        {
-          svn_branch_instance_t *branch;
-
-          SVN_ERR(svn_branch_instance_parse(&branch, family, rev_root, stream,
-                                            family->pool, scratch_pool));
-          APR_ARRAY_PUSH(branch->rev_root->branch_instances, void *) = branch;
-          if (family->fid == root_fid)
-            {
-              branch->rev_root->root_branch = branch;
-            }
-        }
-    }
-
-  *rev_root_p = rev_root;
-  return SVN_NO_ERROR;
-}
-
-/* Write to STREAM a parseable representation of BRANCH.
- */
-static svn_error_t *
-svn_branch_instance_serialize(svn_stream_t *stream,
-                              svn_branch_instance_t *branch,
-                              apr_pool_t *scratch_pool)
-{
-  svn_branch_family_t *family = branch->sibling_defn->family;
-  const char *branch_root_rrpath = svn_branch_get_root_rrpath(branch);
-  int eid;
-
-  SVN_ERR(svn_stream_printf(stream, scratch_pool,
-                            "f%db%d: root-eid %d at %s\n",
-                            family->fid, branch->sibling_defn->bid,
-                            branch->sibling_defn->root_eid,

[... 1215 lines stripped ...]


Mime
View raw message