subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From julianf...@apache.org
Subject svn commit: r1639592 [3/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
Modified: subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c?rev=1639592&r1=1639591&r2=1639592&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_delta/compat3.c Fri Nov 14 10:05:32 2014
@@ -41,17 +41,6 @@
 /* Verify EXPR is true; raise an error if not. */
 #define VERIFY(expr) SVN_ERR_ASSERT(expr)
 
-#ifdef SVN_DEBUG
-/* Return a human-readable string representation of LOC. */
-static const char *
-peg_path_str(svn_editor3_peg_path_t loc,
-             apr_pool_t *result_pool)
-{
-  return apr_psprintf(result_pool, "%s@%ld",
-                      loc.relpath, loc.rev);
-}
-#endif
-
 /*
  * ========================================================================
  * Configuration Options
@@ -1703,3085 +1692,719 @@ find_move(apr_hash_t *moves,
 
 /*
  * ========================================================================
- * Element-Based Branching
+ * Driving the Delta Editor
  * ========================================================================
  */
 
-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)
+/* Information needed for driving the delta editor. */
+typedef struct ev3_from_delta_baton_t
 {
-  int eid = family->next_eid++;
+  /* The Ev1 "delta editor" */
+  const svn_delta_editor_t *deditor;
+  void *dedit_baton;
 
-  return eid;
-}
+  /* Callbacks */
+  svn_editor3__shim_fetch_func_t fetch_func;
+  void *fetch_baton;
 
-/* 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;
+  /* The Ev1 root directory baton if we have opened the root, else null. */
+  void *ev1_root_dir_baton;
 
-  return family;
-}
+#ifdef SHIM_WITH_ABS_PATHS
+  const svn_boolean_t *make_abs_paths;
+#endif
 
-/* 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);
+  /* Repository root URL
+     ### Some code allows this to be null -- but is that valid? */
+  const char *repos_root_url;
+  /* Path of the root of the edit, relative to the repository root. */
+  const char *base_relpath;
 
-  /* 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. */
+  /* Ev1 changes recorded so far: REPOS_RELPATH -> change_node_ev3_t */
+  apr_hash_t *changes;
 
-  /* Register the branch */
-  APR_ARRAY_PUSH(family->branch_siblings, void *) = branch_sibling;
+  /* Moves recorded so far: from_relpath -> (char *)to_relpath. */
+  apr_hash_t *moves;
 
-  return branch_sibling;
-}
+  apr_pool_t *edit_pool;
+} ev3_from_delta_baton_t;
 
-/* Return the branch sibling definition with branch id BID in FAMILY.
+/* Get all the (Ev1) paths that have changes. Return only paths at or below
+ * BASE_RELPATH, and return them relative to BASE_RELPATH.
  *
- * Return NULL if not found.
+ * ### Instead, we should probably avoid adding paths outside BASE_RELPATH
+ *     to CHANGES in the first place, and not allow them here.
  */
-static svn_branch_sibling_t *
-family_find_branch_sibling(svn_branch_family_t *family,
-                           int bid)
+static const apr_array_header_t *
+get_unsorted_paths(apr_hash_t *changes,
+                   const char *base_relpath,
+                   apr_pool_t *scratch_pool)
 {
-  int i;
+  apr_array_header_t *paths
+    = apr_array_make(scratch_pool, apr_hash_count(changes), sizeof(char *));
+  apr_hash_index_t *hi;
 
-  for (i = 0; i < family->branch_siblings->nelts; i++)
+  /* Build a new array with just the paths, trimmed to relative paths for
+     the Ev1 drive.  */
+  for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
     {
-      svn_branch_sibling_t *this
-        = APR_ARRAY_IDX(family->branch_siblings, i, void *);
+      const char *this_path = apr_hash_this_key(hi);
+      const char *this_relpath = svn_relpath_skip_ancestor(base_relpath,
+                                                           this_path);
 
-      if (this->bid == bid)
-        return this;
+      if (this_relpath)
+        {
+          APR_ARRAY_PUSH(paths, const char *) = this_relpath;
+        }
     }
-  return NULL;
+
+  return paths;
 }
 
-/* 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)
+/*  */
+static svn_error_t *
+set_target_revision_ev3(void *edit_baton,
+                        svn_revnum_t target_revision,
+                        apr_pool_t *scratch_pool)
 {
-  svn_branch_sibling_t *sibling = family_find_branch_sibling(family, bid);
+  ev3_from_delta_baton_t *eb = edit_baton;
 
-  if (!sibling)
-    {
-      sibling = family_create_branch_sibling(family, bid, root_eid);
-    }
+  SVN_ERR(eb->deditor->set_target_revision(eb->dedit_baton, target_revision,
+                                           scratch_pool));
 
-  SVN_ERR_ASSERT_NO_RETURN(sibling->root_eid == root_eid);
-  return sibling;
+  return SVN_NO_ERROR;
 }
 
-/* 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)
+/*  */
+static svn_error_t *
+open_root_ev3(void *baton,
+              svn_revnum_t base_revision)
 {
-  int bid = family->next_bid++;
-  svn_branch_sibling_t *branch_sibling
-    = family_create_branch_sibling(family, bid, root_eid);
-
-  return branch_sibling;
-}
+  ev3_from_delta_baton_t *eb = baton;
 
-apr_array_header_t *
-svn_branch_family_get_children(svn_branch_family_t *family,
-                               apr_pool_t *result_pool)
-{
-  return family->sub_families;
+  SVN_ERR(eb->deditor->open_root(eb->dedit_baton, base_revision,
+                                 eb->edit_pool, &eb->ev1_root_dir_baton));
+  return SVN_NO_ERROR;
 }
 
-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)
+/* If RELPATH is a child of a copy, return the path of the copy root,
+ * else return NULL.
+ */
+static const char *
+find_enclosing_copy(apr_hash_t *changes,
+                    const char *relpath,
+                    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++)
+  while (*relpath)
     {
-      svn_branch_instance_t *branch
-        = APR_ARRAY_IDX(rev_branch_instances, i, svn_branch_instance_t *);
+      const change_node_t *change = svn_hash_gets(changes, relpath);
 
-      if (branch->sibling_defn->family == family)
-        APR_ARRAY_PUSH(fam_branch_instances, void *) = branch;
+      if (change)
+        {
+          if (change->copyfrom_path)
+            return relpath;
+          if (change->action != RESTRUCTURE_NONE)
+            return NULL;
+        }
+      relpath = svn_relpath_dirname(relpath, result_pool);
     }
 
-  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;
+  return NULL;
 }
 
-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)
+/* Set *BASE_PROPS to the 'base' properties, against which any changes
+ * will be described, for the changed path described in CHANGES at
+ * REPOS_RELPATH.
+ *
+ * For a copied path, including a copy child path, fetch from the copy
+ * source path. For a plain add, return an empty set. For a delete,
+ * return NULL.
+ */
+static svn_error_t *
+fetch_base_props(apr_hash_t **base_props,
+                 apr_hash_t *changes,
+                 const char *repos_relpath,
+                 svn_editor3__shim_fetch_func_t fetch_func,
+                 void *fetch_baton,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_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;
-}
+  const change_node_t *change = svn_hash_gets(changes, repos_relpath);
+  const char *source_path = NULL;
+  svn_revnum_t source_rev;
 
-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)
+  if (change->action == RESTRUCTURE_DELETE)
     {
-      return FALSE;
+      *base_props = NULL;
     }
 
-  if (content_left->parent_eid != content_right->parent_eid)
+  else if (change->action == RESTRUCTURE_ADD && ! change->copyfrom_path)
     {
-      return FALSE;
+      *base_props = apr_hash_make(result_pool);
     }
-  if (strcmp(content_left->name, content_right->name) != 0)
+  else if (change->copyfrom_path)
     {
-      return FALSE;
+      source_path = change->copyfrom_path;
+      source_rev = change->copyfrom_rev;
     }
-  if (! svn_editor3_node_content_equal(content_left->content,
-                                       content_right->content,
-                                       scratch_pool))
+  else /* RESTRUCTURE_NONE */
     {
-      return FALSE;
-    }
-
-  return TRUE;
-}
+      /* It's an edit, but possibly to a copy child. Discover if it's a
+         copy child, & find the copy-from source. */
 
+      const char *copy_path
+        = find_enclosing_copy(changes, repos_relpath, scratch_pool);
 
-/*
- * ========================================================================
- * Branch mappings
- * ========================================================================
- */
+      if (copy_path)
+        {
+          const change_node_t *enclosing_copy = svn_hash_gets(changes, copy_path);
+          const char *remainder = svn_relpath_skip_ancestor(copy_path, repos_relpath);
 
-const char *
-svn_branch_get_root_rrpath(const svn_branch_instance_t *branch)
-{
-  const char *root_rrpath = branch->branch_root_rrpath;
+          source_path = svn_relpath_join(enclosing_copy->copyfrom_path,
+                                         remainder, scratch_pool);
+          source_rev = enclosing_copy->copyfrom_rev;
+        }
+      else
+        {
+          /* It's a plain edit (not a copy child path). */
+          source_path = repos_relpath;
+          source_rev = change->changing_rev;
+        }
+    }
 
-  SVN_ERR_ASSERT_NO_RETURN(root_rrpath);
-  return root_rrpath;
-}
+  if (source_path)
+    {
+      SVN_ERR(fetch_func(NULL, base_props, NULL, NULL,
+                         fetch_baton, source_path, source_rev,
+                         result_pool, scratch_pool));
+    }
 
-/* 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)));
+  return SVN_NO_ERROR;
 }
 
-/* In BRANCH, get element EID's node (parent, name, content).
+/* Send property changes to Ev1 for the CHANGE at REPOS_RELPATH.
  *
- * If element EID is not present, return null. Otherwise, the returned
- * node's content may be null meaning it is unknown.
+ * Ev1 requires exactly one prop-change call for each prop whose value
+ * has changed. Therefore we *have* to fetch the original props from the
+ * repository, provide them as OLD_PROPS, and calculate the changes.
  */
-static svn_branch_el_rev_content_t *
-branch_map_get(const svn_branch_instance_t *branch,
-               int eid)
+static svn_error_t *
+drive_ev1_props(const char *repos_relpath,
+                const change_node_t *change,
+                apr_hash_t *old_props,
+                const svn_delta_editor_t *deditor,
+                void *node_baton,
+                apr_pool_t *scratch_pool)
 {
-  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);
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_array_header_t *propdiffs;
+  int i;
 
-  node = apr_hash_get(branch->e_map, &eid, sizeof(eid));
+  SVN_ERR_ASSERT(change->action != RESTRUCTURE_DELETE);
 
-  if (node)
-    branch_map_node_validate(branch, eid, node);
-  return node;
-}
+  /* If there are no property changes, then just exit. */
+  if (change->props == NULL)
+    return SVN_NO_ERROR;
 
-/* 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(svn_prop_diffs(&propdiffs, change->props, old_props, scratch_pool));
 
-  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);
+  /* Apply property changes. These should be changes against the empty set
+     for a new node, or changes against the source node for a copied node. */
+  for (i = 0; i < propdiffs->nelts; i++)
+    {
+      const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
 
-  apr_hash_set(branch->e_map, eid_p, sizeof(*eid_p), node);
-}
+      svn_pool_clear(iterpool);
 
-/* 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);
+      if (change->kind == svn_node_dir)
+        SVN_ERR(deditor->change_dir_prop(node_baton,
+                                         prop->name, prop->value,
+                                         iterpool));
+      else
+        SVN_ERR(deditor->change_file_prop(node_baton,
+                                          prop->name, prop->value,
+                                          iterpool));
+    }
 
-  branch_map_set(branch, eid, NULL);
-}
+#ifdef SHIM_WITH_UNLOCK
+  /* Handle the funky unlock protocol. Note: only possibly on files.  */
+  if (change->unlock)
+    {
+      SVN_ERR_ASSERT(change->kind == svn_node_file);
+      SVN_ERR(deditor->change_file_prop(node_baton,
+                                            SVN_PROP_ENTRY_LOCK_TOKEN, NULL,
+                                            iterpool));
+    }
+#endif
 
-/* 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.
+  svn_pool_destroy(iterpool);
+  return SVN_NO_ERROR;
+}
+
+/* Drive the Ev1 editor with the change recorded in EB->changes for the
+ * path EV1_RELPATH (which is relative to EB->base_relpath).
  *
- * Duplicate NEW_NAME into the branch mapping's pool.
+ * Conforms to svn_delta_path_driver_cb_func_t.
  */
-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)
+static svn_error_t *
+apply_change(void **dir_baton,
+             void *parent_baton,
+             void *callback_baton,
+             const char *ev1_relpath,
+             apr_pool_t *result_pool)
 {
-  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);
+  apr_pool_t *scratch_pool = result_pool;
+  const ev3_from_delta_baton_t *eb = callback_baton;
+  const char *relpath = svn_relpath_join(eb->base_relpath, ev1_relpath,
+                                         scratch_pool);
+  const change_node_t *change = svn_hash_gets(eb->changes, relpath);
+  void *file_baton = NULL;
+  apr_hash_t *base_props;
 
-  /* 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! */
+  /* The callback should only be called for paths in CHANGES.  */
+  SVN_ERR_ASSERT(change != NULL);
 
-  /* Insert the new version */
-  branch_map_set(branch, eid, node);
-}
+  /* Typically, we are not creating new directory batons.  */
+  *dir_baton = NULL;
 
-/* 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;
+  SVN_ERR(fetch_base_props(&base_props, eb->changes, relpath,
+                           eb->fetch_func, eb->fetch_baton,
+                           scratch_pool, scratch_pool));
 
-  do
+  /* Are we editing the root of the tree?  */
+  if (parent_baton == NULL)
     {
-      changed = FALSE;
+      /* The root dir was already opened. */
+      *dir_baton = eb->ev1_root_dir_baton;
 
-      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);
+      /* Only property edits are allowed on the root.  */
+      SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
+      SVN_ERR(drive_ev1_props(relpath, change, base_props,
+                              eb->deditor, *dir_baton, scratch_pool));
 
-          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;
-            }
-        }
+      /* No further action possible for the root.  */
+      return SVN_NO_ERROR;
     }
-  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)
+  if (change->action == RESTRUCTURE_DELETE)
     {
-      node = branch_map_get(branch, eid);
-      if (! node)
-        return NULL;
-      path = svn_relpath_join(node->name, path, result_pool);
+      SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting_rev,
+                                        parent_baton, scratch_pool));
+
+      /* No futher action possible for this node.  */
+      return SVN_NO_ERROR;
     }
-  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 we're not deleting this node, then we should know its kind.  */
+  SVN_ERR_ASSERT(change->kind != svn_node_unknown);
 
-  if (path)
+#ifdef SHIM_WITH_ADD_ABSENT
+  if (change->action == RESTRUCTURE_ADD_ABSENT)
     {
-      rrpath = svn_relpath_join(svn_branch_get_root_rrpath(branch),
-                                path, result_pool);
-    }
-  return rrpath;
-}
+      if (change->kind == svn_node_dir)
+        SVN_ERR(eb->deditor->absent_directory(ev1_relpath, parent_baton,
+                                              scratch_pool));
+      else if (change->kind == svn_node_file)
+        SVN_ERR(eb->deditor->absent_file(ev1_relpath, parent_baton,
+                                         scratch_pool));
+      else
+        SVN_ERR_MALFUNCTION();
 
-/* 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;
+      /* No further action possible for this node.  */
+      return SVN_NO_ERROR;
+    }
+#endif
+  /* RESTRUCTURE_NONE or RESTRUCTURE_ADD  */
 
-  /* ### This is a crude, linear search */
-  for (hi = apr_hash_first(scratch_pool, branch->e_map);
-       hi; hi = apr_hash_next(hi))
+  if (change->action == RESTRUCTURE_ADD)
     {
-      int eid = *(const int *)apr_hash_this_key(hi);
-      const char *this_path = svn_branch_get_path_by_eid(branch, eid,
-                                                         scratch_pool);
+      const char *copyfrom_url = NULL;
+      svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
 
-      if (! this_path)
-        {
-          /* Mapping is not complete; this element is in effect not present. */
-          continue;
-        }
-      if (strcmp(path, this_path) == 0)
+      /* Do we have an old node to delete first? If so, delete it. */
+      if (change->deleting)
+        SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting_rev,
+                                          parent_baton, scratch_pool));
+
+      /* If it's a copy, determine the copy source location. */
+      if (change->copyfrom_path)
         {
-          return eid;
-        }
-    }
+          /* ### What's this about URL vs. fspath? REPOS_ROOT_URL isn't
+             optional, is it, at least in a commit editor? */
+          if (eb->repos_root_url)
+            copyfrom_url = svn_path_url_add_component2(eb->repos_root_url,
+                                                       change->copyfrom_path,
+                                                       scratch_pool);
+          else
+            {
+              copyfrom_url = change->copyfrom_path;
 
-  return -1;
-}
+              /* Make this an FS path by prepending "/" */
+              if (copyfrom_url[0] != '/')
+                copyfrom_url = apr_pstrcat(scratch_pool, "/",
+                                           copyfrom_url, SVN_VA_NULL);
+            }
 
-/* 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;
+          copyfrom_rev = change->copyfrom_rev;
+        }
 
-  if (path)
-    {
-      eid = branch_map_get_eid_by_path(branch, path, scratch_pool);
+      if (change->kind == svn_node_dir)
+        SVN_ERR(eb->deditor->add_directory(ev1_relpath, parent_baton,
+                                           copyfrom_url, copyfrom_rev,
+                                           result_pool, dir_baton));
+      else if (change->kind == svn_node_file)
+        SVN_ERR(eb->deditor->add_file(ev1_relpath, parent_baton,
+                                      copyfrom_url, copyfrom_rev,
+                                      result_pool, &file_baton));
+      else
+        SVN_ERR_MALFUNCTION();
     }
-  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)
+  else /* RESTRUCTURE_NONE */
     {
-      svn_editor3_peg_path_t peg;
+      /* ### The code that inserts a "plain edit" change record sets
+         'changing_rev' to the peg rev of the pegged part of the path,
+         even when the full path refers to a child of a copy. Should we
+         instead be using the copy source rev here, in that case? (Like
+         when we fetch the base properties.) */
 
-      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);
+      if (change->kind == svn_node_dir)
+        SVN_ERR(eb->deditor->open_directory(ev1_relpath, parent_baton,
+                                            change->changing_rev,
+                                            result_pool, dir_baton));
+      else if (change->kind == svn_node_file)
+        SVN_ERR(eb->deditor->open_file(ev1_relpath, parent_baton,
+                                       change->changing_rev,
+                                       result_pool, &file_baton));
+      else
+        SVN_ERR_MALFUNCTION();
     }
-  *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;
+  /* Apply any properties in CHANGE to the node.  */
+  if (change->kind == svn_node_dir)
+    SVN_ERR(drive_ev1_props(relpath, change, base_props,
+                            eb->deditor, *dir_baton, scratch_pool));
+  else
+    SVN_ERR(drive_ev1_props(relpath, change, base_props,
+                            eb->deditor, file_baton, scratch_pool));
 
-  for (hi = apr_hash_first(scratch_pool, branch->e_map);
-       hi; hi = apr_hash_next(hi))
+  /* Send the text content delta, if new text content is provided. */
+  if (change->contents_text)
     {
-      int this_eid = *(const int *)apr_hash_this_key(hi);
-      svn_branch_el_rev_content_t *this_node = apr_hash_this_val(hi);
+      svn_stream_t *read_stream;
+      svn_txdelta_window_handler_t handler;
+      void *handler_baton;
 
-      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));
+      read_stream = svn_stream_from_stringbuf(change->contents_text,
+                                              scratch_pool);
+      /* ### would be nice to have a BASE_CHECKSUM, but hey: this is the
+         ### shim code...  */
+      SVN_ERR(eb->deditor->apply_textdelta(file_baton, NULL, scratch_pool,
+                                           &handler, &handler_baton));
+      /* ### it would be nice to send a true txdelta here, but whatever.  */
+      SVN_ERR(svn_txdelta_send_stream(read_stream, handler, handler_baton,
+                                      NULL, scratch_pool));
+      SVN_ERR(svn_stream_close(read_stream));
+    }
 
-          /* Delete this immediate child. */
-          branch_map_delete(branch, this_eid);
-        }
+  if (file_baton)
+    {
+      SVN_ERR(eb->deditor->close_file(file_baton, NULL, scratch_pool));
     }
 
   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.
+
+/* Drive the Ev1 with all the changes we have accumulated in EB.
  *
- * FROM_BRANCH and TO_BRANCH may be the same or different branch instances
- * in the same or different branch families.
+ * We visit each path operated on, and any ancestor directories, in an order
+ * that is depth first and in lexical order within each directory.
  *
- * 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.
+ * ### For an update editor, we want to send all deletes before all adds
+ *     to make case-only renames work better on case-insensitive systems.
+ *     But for a commit editor that is irrelevant.
  *
- * 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.
+ * ### The Ev2-to-Ev1 converter ordered changes such that lone deletes come
+ *     before all other changes, but a delete that is part of a replacement
+ *     was sent immediately before the replacing add. I don't know why, but
+ *     I can't see how that could be right.
  */
 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)
+drive_changes(ev3_from_delta_baton_t *eb,
+              apr_pool_t *scratch_pool)
 {
-  apr_hash_index_t *hi;
+  change_node_t *change;
+  const apr_array_header_t *paths;
 
-  /* 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));
+  /* If the driver has not explicitly opened the root directory, do so now. */
+  if (eb->ev1_root_dir_baton == NULL)
+    SVN_ERR(open_root_ev3(eb, SVN_INVALID_REVNUM));
 
-  /* 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))
+  /* Make the path driver visit the root dir of the edit. Otherwise, it
+     will attempt an open_root() instead, which we already did. */
+  /* ### Seems clumsy. Is there not a simpler way? */
+  if (! svn_hash_gets(eb->changes, eb->base_relpath))
     {
-      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));
-        }
+      SVN_ERR(insert_change(&change, eb->changes, eb->base_relpath,
+                            RESTRUCTURE_NONE));
+      change->kind = svn_node_dir;
     }
+  /* No property changes (tho they might exist from a real change).  */
+
+  /* Get a list of Ev1-relative paths (unsorted). */
+  paths = get_unsorted_paths(eb->changes, eb->base_relpath, scratch_pool);
+  SVN_ERR(svn_delta_path_driver2(eb->deditor, eb->dedit_baton,
+                                 paths, TRUE /*sort*/,
+                                 apply_change, (void *)eb,
+                                 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));
+/*
+ * ===================================================================
+ * Commit Editor v3 (incremental tree changes; path-based addressing)
+ * ===================================================================
+ */
 
-  /* 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.
+/* Return the current path in txn corresponding to the given peg location
+ * PEG_LOC. Follow moves that have been made so far.
  */
-static svn_error_t *
-branch_instance_delete_r(svn_branch_instance_t *branch,
-                         apr_pool_t *scratch_pool)
+static const char *
+e3_pegged_path_in_txn(ev3_from_delta_baton_t *eb,
+                      svn_editor3_peg_path_t peg_loc,
+                      apr_pool_t *scratch_pool)
 {
-  apr_array_header_t *subbranches
-    = branch_get_all_sub_branches(branch, scratch_pool, scratch_pool);
-  int i;
+  const char *p;
 
-  /* Delete nested branch instances, recursively */
-  for (i = 0; i < subbranches->nelts; i++)
+  if (SVN_IS_VALID_REVNUM(peg_loc.rev))
     {
-      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;
+      p = find_move(eb->moves, peg_loc.relpath, scratch_pool);
     }
   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,
-                            branch_root_rrpath[0] ? branch_root_rrpath : "."));
-
-  branch_map_purge_orphans(branch, scratch_pool);
-  for (eid = family->first_eid; eid < family->next_eid; eid++)
-    {
-      svn_branch_el_rev_content_t *node = branch_map_get(branch, eid);
-      int parent_eid;
-      const char *name;
-      const char *path;
-
-      if (node)
-        {
-          path = svn_branch_get_path_by_eid(branch, eid, scratch_pool);
-          SVN_ERR_ASSERT(path);
-          parent_eid = node->parent_eid;
-          name = node->name[0] ? node->name : ".";
-          path = path[0] ? path : ".";
-        }
-      else
-        {
-          /* ### TODO: instead, omit the line completely; but the
-                 parser currently can't handle that. */
-          parent_eid = -1;
-          name = "(null)";
-          path = "(null)";
-        }
-      SVN_ERR(svn_stream_printf(stream, scratch_pool,
-                                "f%db%de%d: %d %s %s\n",
-                                family->fid, branch->sibling_defn->bid, eid,
-                                parent_eid, name, path));
-    }
-  return SVN_NO_ERROR;
-}
-
-/* Write to STREAM a parseable representation of FAMILY whose parent
- * family id is PARENT_FID. Recursively write all sub-families.
- */
-static svn_error_t *
-svn_branch_family_serialize(svn_stream_t *stream,
-                            svn_branch_revision_root_t *rev_root,
-                            svn_branch_family_t *family,
-                            int parent_fid,
-                            apr_pool_t *scratch_pool)
-{
-  int i;
-
-  SVN_ERR(svn_stream_printf(stream, scratch_pool,
-                            "f%d: bids %d %d eids %d %d parent-fid %d\n",
-                            family->fid,
-                            family->first_bid, family->next_bid,
-                            family->first_eid, family->next_eid,
-                            parent_fid));
-
-  for (i = 0; i < rev_root->branch_instances->nelts; i++)
-    {
-      svn_branch_instance_t *branch
-        = APR_ARRAY_IDX(rev_root->branch_instances, i, void *);
-
-      if (branch->sibling_defn->family == family)
-        {
-          SVN_ERR(svn_branch_instance_serialize(stream, branch, scratch_pool));
-        }
-    }
-
-  for (i = 0; i < family->sub_families->nelts; i++)
-    {
-      svn_branch_family_t *f = APR_ARRAY_IDX(family->sub_families, i, void *);
-
-      SVN_ERR(svn_branch_family_serialize(stream, rev_root, f, family->fid,
-                                          scratch_pool));
+      /* Path in txn is just as specified */
+      p = peg_loc.relpath;
     }
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_branch_revision_root_serialize(svn_stream_t *stream,
-                                   svn_branch_revision_root_t *rev_root,
-                                   int next_fid,
-                                   apr_pool_t *scratch_pool)
-{
-  SVN_ERR(svn_stream_printf(stream, scratch_pool,
-                            "r%ld: fids %d %d root-fid %d\n",
-                            rev_root->rev,
-                            0, next_fid,
-                            rev_root->root_branch->sibling_defn->family->fid));
-
-  SVN_ERR(svn_branch_family_serialize(
-            stream, rev_root, rev_root->root_branch->sibling_defn->family,
-            -1 /*parent_fid*/, scratch_pool));
 
-  return SVN_NO_ERROR;
+  return p;
 }
 
-
-/*
- * ========================================================================
- */
-
-static svn_error_t *
-svn_branch_el_rev_get(svn_branch_el_rev_content_t **node_p,
-                      svn_editor3_t *editor,
-                      svn_branch_instance_t *branch,
-                      int eid,
-                      apr_pool_t *result_pool,
-                      apr_pool_t *scratch_pool);
-
-/* Find the (deepest) branch of which the path RRPATH is either the root
- * path or a normal, non-sub-branch path. An element need not exist at
- * RRPATH.
- *
- * Set *BRANCH_P to the deepest branch within ROOT_BRANCH (recursively,
- * including itself) that contains the path RRPATH.
- *
- * If EID_P is not null then set *EID_P to the element id of RRPATH in
- * *BRANCH_P, or to -1 if no element exists at RRPATH in that branch.
+/* Return the current path in txn corresponding to LOC.
  *
- * If RRPATH is not within any branch in ROOT_BRANCH, set *BRANCH_P to
- * NULL and (if EID_P is not null) *EID_P to -1.
+ * LOC represents a path pegged to a revision, plus a created path
+ * relative to the pegged path. Follow the pegged part of the path
+ * through moves that have been made so far.
  */
-static void
-svn_branch_find_nested_branch_element_by_rrpath(
-                                svn_branch_instance_t **branch_p,
-                                int *eid_p,
-                                svn_branch_instance_t *root_branch,
-                                const char *rrpath,
-                                apr_pool_t *scratch_pool)
+static const char *
+e3_general_path_in_txn(ev3_from_delta_baton_t *eb,
+                       svn_editor3_txn_path_t loc,
+                       apr_pool_t *result_pool)
 {
-  const char *branch_root_path = svn_branch_get_root_rrpath(root_branch);
-  apr_array_header_t *branch_instances;
-  int i;
-
-  if (! svn_relpath_skip_ancestor(branch_root_path, rrpath))
-    {
-      /* The path we're looking for is not (path-wise) in this branch. */
-      *branch_p = NULL;
-      if (eid_p)
-        *eid_p = -1;
-      return;
-    }
-
-  /* The path we're looking for is (path-wise) in this branch. See if it
-     is also in a sub-branch (recursively). */
-  branch_instances = branch_get_all_sub_branches(root_branch,
-                                                 scratch_pool, scratch_pool);
-  for (i = 0; i < branch_instances->nelts; i++)
-    {
-      svn_branch_instance_t *this_branch
-        = APR_ARRAY_IDX(branch_instances, i, void *);
-      svn_branch_instance_t *sub_branch;
-      int sub_branch_eid;
-
-      svn_branch_find_nested_branch_element_by_rrpath(&sub_branch, &sub_branch_eid,
-                                                    this_branch, rrpath,
-                                                    scratch_pool);
-      if (sub_branch)
-        {
-           *branch_p = sub_branch;
-           if (eid_p)
-             *eid_p = sub_branch_eid;
-           return;
-         }
-    }
-
-  *branch_p = root_branch;
-  if (eid_p)
-    *eid_p = branch_map_get_eid_by_rrpath(root_branch, rrpath, scratch_pool);
+  return svn_relpath_join(e3_pegged_path_in_txn(eb, loc.peg, result_pool),
+                          loc.relpath, result_pool);
 }
 
-/* Adjust BRANCH and its subbranches (recursively),
- * to reflect deletion of the subtree at EID.
- *
- * Element EID MUST be the location of a non-root element of BRANCH.
- * If EID is the root of a subbranch and/or contains nested
- * subbranches, also delete them.
- */
+/* An #svn_editor3_t method. */
 static svn_error_t *
-branch_delete_subtree_r(svn_branch_instance_t *branch,
-                        int eid,
-                        apr_pool_t *scratch_pool)
+editor3_mk(void *baton,
+           svn_node_kind_t new_kind,
+           svn_editor3_txn_path_t new_parent_loc,
+           const char *new_name,
+           apr_pool_t *scratch_pool)
 {
-  apr_array_header_t *subbranches;
-  int i;
+  ev3_from_delta_baton_t *eb = baton;
+  change_node_t *change;
 
-  /* Delete any nested subbranches at or inside EID. */
-  subbranches = branch_get_sub_branches(branch, eid, scratch_pool, scratch_pool);
-  for (i = 0; i < subbranches->nelts; i++)
-    {
-      svn_branch_instance_t *subbranch
-        = APR_ARRAY_IDX(subbranches, i, void *);
+  /* look up parent_loc in shadow txn */
+  const char *new_parent_txnpath
+    = e3_general_path_in_txn(eb, new_parent_loc, scratch_pool);
+  const char *new_txnpath
+    = svn_relpath_join(new_parent_txnpath, new_name, scratch_pool);
 
-      /* Delete the whole subbranch (recursively) */
-      SVN_ERR(branch_instance_delete_r(subbranch, scratch_pool));
-    }
+  /* Precondition: a child with this name in parent_loc must not exist,
+     as far as we know. This is checked by insert_change(). */
 
-  /* update the element mapping in this branch */
-  branch_map_delete(branch, eid /* ### , since_rev? */);
-  /* ### TODO: delete all elements under EID too. */
+  /* create node in shadow txn */
+  SVN_ERR(insert_change(&change, eb->changes, new_txnpath, RESTRUCTURE_ADD));
+  change->kind = new_kind;
 
   return SVN_NO_ERROR;
 }
 
-/* Adjust TO_OUTER_BRANCH and its subbranches (recursively),
- * to reflect branching a subtree from FROM_BRANCH:FROM_EID to
- * create a new subbranch of TO_OUTER_BRANCH at TO_OUTER_PARENT_EID:NEW_NAME.
- *
- * FROM_BRANCH must be an immediate child branch of OUTER_BRANCH.
- *
- * FROM_BRANCH:FROM_EID must be an existing element. It may be the
- * root of FROM_BRANCH. It must not be the root of a subbranch of
- * FROM_BRANCH.
- *
- * TO_OUTER_BRANCH:TO_OUTER_PARENT_EID must be an existing directory
- * and NEW_NAME must be nonexistent in that directory.
- */
+/* An #svn_editor3_t method. */
 static svn_error_t *
-branch_branch_subtree_r(svn_branch_instance_t **new_branch_p,
-                        svn_branch_instance_t *from_branch,
-                        int from_eid,
-                        svn_branch_instance_t *to_outer_branch,
-                        svn_editor3_eid_t to_outer_parent_eid,
-                        const char *new_name,
-                        apr_pool_t *scratch_pool)
-{
-  int to_outer_eid;
-  svn_branch_sibling_t *new_branch_def;
-  svn_branch_instance_t *new_branch;
-
-  /* FROM_BRANCH must be an immediate child branch of OUTER_BRANCH. */
-  /* SVN_ERR_ASSERT(from_branch->sibling_defn->family->parent_family->fid
-                 == to_outer_branch->sibling_defn->family->fid); */
-
-  /* SVN_ERR_ASSERT(...); */
-
-  /* assign new eid to root node (outer branch) */
-  to_outer_eid = family_add_new_element(to_outer_branch->sibling_defn->family);
-  branch_map_update_as_subbranch_root(to_outer_branch, to_outer_eid,
-                                      to_outer_parent_eid, new_name);
-
-  /* create new inner branch sibling & instance */
-  /* ### On sub-branches, should not add new branch sibling, only instance. */
-  new_branch_def
-    = family_add_new_branch_sibling(from_branch->sibling_defn->family,
-                                    from_eid);
-  new_branch = branch_add_new_branch_instance(to_outer_branch, to_outer_eid,
-                                              new_branch_def, scratch_pool);
-
-  /* Initialize the new (inner) branch root element */
-  {
-    svn_editor3_node_content_t *old_content;
-
-    SVN_ERR(copy_content_from(&old_content,
-                              from_branch, from_eid,
-                              scratch_pool, scratch_pool));
-    branch_map_update(new_branch, new_branch_def->root_eid,
-                      -1, "", old_content);
-  }
-
-  /* Populate the rest of the new branch mapping */
-  SVN_ERR(branch_map_branch_children(from_branch, from_eid,
-                                     new_branch, new_branch_def->root_eid,
-                                     scratch_pool));
-
-  /* branch any subbranches under FROM_BRANCH:FROM_EID */
-#if 0 /* ### Later. */
-  {
-    apr_array_header_t *subbranches;
-    int i;
-
-    subbranches = branch_get_sub_branches(from_branch, from_eid,
-                                          scratch_pool, scratch_pool);
-    for (i = 0; i < subbranches->nelts; i++)
-      {
-        svn_branch_instance_t *subbranch
-          = APR_ARRAY_IDX(subbranches, i, void *);
-        int new_parent_eid /* = ### */;
-        const char *new_name /* = ### */;
-
-        /* branch this subbranch into NEW_BRANCH (recursing) */
-        SVN_ERR(branch_branch_subtree_r(NULL,
-                                        subbranch,
-                                        subbranch->sibling_defn->root_eid,
-                                        new_branch, new_parent_eid, new_name,
-                                        scratch_pool));
-      }
-  }
+editor3_cp(void *baton,
+#ifdef SVN_EDITOR3_WITH_COPY_FROM_THIS_REV
+           svn_editor3_txn_path_t from_txn_loc,
+#else
+           svn_editor3_peg_path_t from_peg_loc,
 #endif
-
-  if (new_branch_p)
-    *new_branch_p = new_branch;
-  return SVN_NO_ERROR;
-}
-
-/* Adjust TO_BRANCH and its subbranches (recursively), to reflect a copy
- * of a subtree from FROM_EL_REV to TO_PARENT_EID:TO_NAME.
- *
- * FROM_EL_REV must be an existing element. (It may be a branch root.)
- *
- * ### TODO:
- * If FROM_EL_REV is the root of a subbranch and/or contains nested
- * subbranches, also copy them ...
- * ### What shall we do with a subbranch? Make plain copies of its raw
- *     elements; make a subbranch by branching the source subbranch in
- *     cases where possible; make a subbranch in a new family?
- *
- * TO_PARENT_EID must be a directory element in TO_BRANCH, and TO_NAME a
- * non-existing path in it.
- */
-static svn_error_t *
-branch_copy_subtree_r(const svn_branch_el_rev_id_t *from_el_rev,
-                      svn_branch_instance_t *to_branch,
-                      svn_editor3_eid_t to_parent_eid,
-                      const char *to_name,
-                      apr_pool_t *scratch_pool)
+           svn_editor3_txn_path_t new_parent_loc,
+           const char *new_name,
+           apr_pool_t *scratch_pool)
 {
-  int to_eid;
-  svn_branch_el_rev_content_t *old_content;
-
-  /* Copy the root element */
-  to_eid = family_add_new_element(to_branch->sibling_defn->family);
-  old_content = branch_map_get(from_el_rev->branch, from_el_rev->eid);
-
-  /* ### If this element is a subbranch root, need to call
-         branch_map_update_as_subbranch_root() instead. */
-  branch_map_update(to_branch, to_eid,
-                    to_parent_eid, to_name, old_content->content);
-
-  /* Copy the children within this branch */
-  SVN_ERR(branch_map_copy_children(from_el_rev->branch, from_el_rev->eid,
-                                   to_branch, to_eid,
-                                   scratch_pool));
-
-  /* handle any subbranches under FROM_BRANCH:FROM_EID */
-  /* ### Later. */
-
-  SVN_DBG(("cp subtree from e%d (%d/%s) to e%d (%d/%s)",
-           from_el_rev->eid, old_content->parent_eid, old_content->name,
-           to_eid, to_parent_eid, to_name));
-  return SVN_NO_ERROR;
-}
-
-/* Return the relative path to element EID within SUBTREE.
- *
- * Assumes the mapping is "complete" (has complete paths to SUBTREE and to EID).
- */
-static const char *
-element_relpath_in_subtree(const svn_branch_el_rev_id_t *subtree,
-                           int eid,
-                           apr_pool_t *scratch_pool)
-{
-  const char *subtree_path;
-  const char *element_path;
-  const char *relpath = NULL;
-
-  SVN_ERR_ASSERT_NO_RETURN(
-    subtree->eid >= subtree->branch->sibling_defn->family->first_eid
-    && subtree->eid < subtree->branch->sibling_defn->family->next_eid);
-  SVN_ERR_ASSERT_NO_RETURN(
-    eid >= subtree->branch->sibling_defn->family->first_eid
-    && eid < subtree->branch->sibling_defn->family->next_eid);
-
-  subtree_path = svn_branch_get_path_by_eid(subtree->branch, subtree->eid,
-                                            scratch_pool);
-  element_path = svn_branch_get_path_by_eid(subtree->branch, eid,
-                                            scratch_pool);
-
-  SVN_ERR_ASSERT_NO_RETURN(subtree_path);
-
-  if (element_path)
-    relpath = svn_relpath_skip_ancestor(subtree_path, element_path);
-
-  return relpath;
-}
-
-svn_error_t *
-svn_branch_subtree_differences(apr_hash_t **diff_p,
-                               svn_editor3_t *editor,
-                               const svn_branch_el_rev_id_t *left,
-                               const svn_branch_el_rev_id_t *right,
-                               apr_pool_t *result_pool,
-                               apr_pool_t *scratch_pool)
-{
-  apr_hash_t *diff = apr_hash_make(result_pool);
-  int first_eid, next_eid;
-  int e;
-
-  /*SVN_DBG(("branch_element_differences(b%d r%ld, b%d r%ld, e%d)",
-           left->branch->sibling->bid, left->rev,
-           right->branch->sibling->bid, right->rev, right->eid));*/
-  SVN_ERR_ASSERT(left->branch->sibling_defn->family->fid
-                 == right->branch->sibling_defn->family->fid);
-  SVN_ERR_ASSERT_NO_RETURN(
-    left->eid >= left->branch->sibling_defn->family->first_eid
-    && left->eid < left->branch->sibling_defn->family->next_eid);
-  SVN_ERR_ASSERT_NO_RETURN(
-    right->eid >= left->branch->sibling_defn->family->first_eid
-    && right->eid < left->branch->sibling_defn->family->next_eid);
-
-  first_eid = left->branch->sibling_defn->family->first_eid;
-  next_eid = MAX(left->branch->sibling_defn->family->next_eid,
-                 right->branch->sibling_defn->family->next_eid);
-
-  for (e = first_eid; e < next_eid; e++)
-    {
-      svn_branch_el_rev_content_t *content_left = NULL;
-      svn_branch_el_rev_content_t *content_right = NULL;
-
-      if (e < left->branch->sibling_defn->family->next_eid
-          && element_relpath_in_subtree(left, e, scratch_pool))
-        {
-          SVN_ERR(svn_branch_el_rev_get(&content_left, editor,
-                                        left->branch, e,
-                                        result_pool, scratch_pool));
-        }
-      if (e < right->branch->sibling_defn->family->next_eid
-          && element_relpath_in_subtree(right, e, scratch_pool))
-        {
-          SVN_ERR(svn_branch_el_rev_get(&content_right, editor,
-                                        right->branch, e,
-                                        result_pool, scratch_pool));
-        }
+  ev3_from_delta_baton_t *eb = baton;
+  change_node_t *change;
 
-      if (! svn_branch_el_rev_content_equal(content_left, content_right, scratch_pool))
-        {
-          int *eid_stored = apr_pmemdup(result_pool, &e, sizeof(e));
-          svn_branch_el_rev_content_t **contents
-            = apr_palloc(result_pool, 2 * sizeof(void *));
-
-          contents[0] = content_left;
-          contents[1] = content_right;
-          apr_hash_set(diff, eid_stored, sizeof(*eid_stored), contents);
-        }
-    }
-
-  *diff_p = diff;
-  return SVN_NO_ERROR;
-}
-
-
-/*
- * ========================================================================
- * Driving the Delta Editor
- * ========================================================================
- */
-
-/* Information needed for driving the delta editor. */
-typedef struct ev3_from_delta_baton_t
-{
-  /* The Ev1 "delta editor" */
-  const svn_delta_editor_t *deditor;
-  void *dedit_baton;
-
-  /* Callbacks */
-  svn_editor3__shim_fetch_func_t fetch_func;
-  void *fetch_baton;
-
-  /* The Ev1 root directory baton if we have opened the root, else null. */
-  void *ev1_root_dir_baton;
-
-#ifdef SHIM_WITH_ABS_PATHS
-  const svn_boolean_t *make_abs_paths;
-#endif
-
-  /* Repository root URL
-     ### Some code allows this to be null -- but is that valid? */
-  const char *repos_root_url;
-  /* Path of the root of the edit, relative to the repository root. */
-  const char *base_relpath;
-
-  /* Ev1 changes recorded so far: REPOS_RELPATH -> change_node_ev3_t */
-  apr_hash_t *changes;
-
-  /* Moves recorded so far: from_relpath -> (char *)to_relpath. */
-  apr_hash_t *moves;
-
-  /* The branching state on which the per-element API is working */
-  svn_branch_revision_root_t *edited_rev_root;
-
-  apr_pool_t *edit_pool;
-} ev3_from_delta_baton_t;
-
-/* Get all the (Ev1) paths that have changes. Return only paths at or below
- * BASE_RELPATH, and return them relative to BASE_RELPATH.
- *
- * ### Instead, we should probably avoid adding paths outside BASE_RELPATH
- *     to CHANGES in the first place, and not allow them here.
- */
-static const apr_array_header_t *
-get_unsorted_paths(apr_hash_t *changes,
-                   const char *base_relpath,
-                   apr_pool_t *scratch_pool)
-{
-  apr_array_header_t *paths
-    = apr_array_make(scratch_pool, apr_hash_count(changes), sizeof(char *));
-  apr_hash_index_t *hi;
-
-  /* Build a new array with just the paths, trimmed to relative paths for
-     the Ev1 drive.  */
-  for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
-    {
-      const char *this_path = apr_hash_this_key(hi);
-      const char *this_relpath = svn_relpath_skip_ancestor(base_relpath,
-                                                           this_path);
-
-      if (this_relpath)
-        {
-          APR_ARRAY_PUSH(paths, const char *) = this_relpath;
-        }
-    }
-
-  return paths;
-}
-
-/*  */
-static svn_error_t *
-set_target_revision_ev3(void *edit_baton,
-                        svn_revnum_t target_revision,
-                        apr_pool_t *scratch_pool)
-{
-  ev3_from_delta_baton_t *eb = edit_baton;
-
-  SVN_ERR(eb->deditor->set_target_revision(eb->dedit_baton, target_revision,
-                                           scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/*  */
-static svn_error_t *
-open_root_ev3(void *baton,
-              svn_revnum_t base_revision)
-{
-  ev3_from_delta_baton_t *eb = baton;
-
-  SVN_ERR(eb->deditor->open_root(eb->dedit_baton, base_revision,
-                                 eb->edit_pool, &eb->ev1_root_dir_baton));
-  return SVN_NO_ERROR;
-}
-
-/* If RELPATH is a child of a copy, return the path of the copy root,
- * else return NULL.
- */
-static const char *
-find_enclosing_copy(apr_hash_t *changes,
-                    const char *relpath,
-                    apr_pool_t *result_pool)
-{
-  while (*relpath)
-    {
-      const change_node_t *change = svn_hash_gets(changes, relpath);
-
-      if (change)
-        {
-          if (change->copyfrom_path)
-            return relpath;
-          if (change->action != RESTRUCTURE_NONE)
-            return NULL;
-        }
-      relpath = svn_relpath_dirname(relpath, result_pool);
-    }
-
-  return NULL;
-}
-
-/* Set *BASE_PROPS to the 'base' properties, against which any changes
- * will be described, for the changed path described in CHANGES at
- * REPOS_RELPATH.
- *
- * For a copied path, including a copy child path, fetch from the copy
- * source path. For a plain add, return an empty set. For a delete,
- * return NULL.
- */
-static svn_error_t *
-fetch_base_props(apr_hash_t **base_props,
-                 apr_hash_t *changes,
-                 const char *repos_relpath,
-                 svn_editor3__shim_fetch_func_t fetch_func,
-                 void *fetch_baton,
-                 apr_pool_t *result_pool,
-                 apr_pool_t *scratch_pool)
-{
-  const change_node_t *change = svn_hash_gets(changes, repos_relpath);
-  const char *source_path = NULL;
-  svn_revnum_t source_rev;
-
-  if (change->action == RESTRUCTURE_DELETE)
-    {
-      *base_props = NULL;
-    }
-
-  else if (change->action == RESTRUCTURE_ADD && ! change->copyfrom_path)
-    {
-      *base_props = apr_hash_make(result_pool);
-    }
-  else if (change->copyfrom_path)
-    {
-      source_path = change->copyfrom_path;
-      source_rev = change->copyfrom_rev;
-    }
-  else /* RESTRUCTURE_NONE */
-    {
-      /* It's an edit, but possibly to a copy child. Discover if it's a
-         copy child, & find the copy-from source. */
-
-      const char *copy_path
-        = find_enclosing_copy(changes, repos_relpath, scratch_pool);
-
-      if (copy_path)
-        {
-          const change_node_t *enclosing_copy = svn_hash_gets(changes, copy_path);
-          const char *remainder = svn_relpath_skip_ancestor(copy_path, repos_relpath);
-
-          source_path = svn_relpath_join(enclosing_copy->copyfrom_path,
-                                         remainder, scratch_pool);
-          source_rev = enclosing_copy->copyfrom_rev;
-        }
-      else
-        {
-          /* It's a plain edit (not a copy child path). */
-          source_path = repos_relpath;
-          source_rev = change->changing_rev;
-        }
-    }
-
-  if (source_path)
-    {
-      SVN_ERR(fetch_func(NULL, base_props, NULL, NULL,
-                         fetch_baton, source_path, source_rev,
-                         result_pool, scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Send property changes to Ev1 for the CHANGE at REPOS_RELPATH.
- *
- * Ev1 requires exactly one prop-change call for each prop whose value
- * has changed. Therefore we *have* to fetch the original props from the
- * repository, provide them as OLD_PROPS, and calculate the changes.
- */
-static svn_error_t *
-drive_ev1_props(const char *repos_relpath,
-                const change_node_t *change,
-                apr_hash_t *old_props,
-                const svn_delta_editor_t *deditor,
-                void *node_baton,
-                apr_pool_t *scratch_pool)
-{
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-  apr_array_header_t *propdiffs;
-  int i;
-
-  SVN_ERR_ASSERT(change->action != RESTRUCTURE_DELETE);
-
-  /* If there are no property changes, then just exit. */
-  if (change->props == NULL)
-    return SVN_NO_ERROR;
-
-  SVN_ERR(svn_prop_diffs(&propdiffs, change->props, old_props, scratch_pool));
-
-  /* Apply property changes. These should be changes against the empty set
-     for a new node, or changes against the source node for a copied node. */
-  for (i = 0; i < propdiffs->nelts; i++)
-    {
-      const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
-
-      svn_pool_clear(iterpool);
-
-      if (change->kind == svn_node_dir)
-        SVN_ERR(deditor->change_dir_prop(node_baton,
-                                         prop->name, prop->value,
-                                         iterpool));
-      else
-        SVN_ERR(deditor->change_file_prop(node_baton,
-                                          prop->name, prop->value,
-                                          iterpool));
-    }
-
-#ifdef SHIM_WITH_UNLOCK
-  /* Handle the funky unlock protocol. Note: only possibly on files.  */
-  if (change->unlock)
-    {
-      SVN_ERR_ASSERT(change->kind == svn_node_file);
-      SVN_ERR(deditor->change_file_prop(node_baton,
-                                            SVN_PROP_ENTRY_LOCK_TOKEN, NULL,
-                                            iterpool));
-    }
-#endif
-
-  svn_pool_destroy(iterpool);
-  return SVN_NO_ERROR;
-}
-
-/* Drive the Ev1 editor with the change recorded in EB->changes for the
- * path EV1_RELPATH (which is relative to EB->base_relpath).
- *
- * 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)
-{
-  apr_pool_t *scratch_pool = result_pool;
-  const ev3_from_delta_baton_t *eb = callback_baton;
-  const char *relpath = svn_relpath_join(eb->base_relpath, ev1_relpath,
-                                         scratch_pool);
-  const change_node_t *change = svn_hash_gets(eb->changes, relpath);
-  void *file_baton = NULL;
-  apr_hash_t *base_props;
-
-  /* The callback should only be called for paths in CHANGES.  */
-  SVN_ERR_ASSERT(change != NULL);
-
-  /* Typically, we are not creating new directory batons.  */
-  *dir_baton = NULL;
-
-  SVN_ERR(fetch_base_props(&base_props, eb->changes, relpath,
-                           eb->fetch_func, eb->fetch_baton,
-                           scratch_pool, scratch_pool));
-
-  /* Are we editing the root of the tree?  */
-  if (parent_baton == NULL)
-    {
-      /* The root dir was already opened. */
-      *dir_baton = eb->ev1_root_dir_baton;
-
-      /* Only property edits are allowed on the root.  */
-      SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
-      SVN_ERR(drive_ev1_props(relpath, change, base_props,
-                              eb->deditor, *dir_baton, scratch_pool));
-
-      /* No further action possible for the root.  */
-      return SVN_NO_ERROR;
-    }
-
-  if (change->action == RESTRUCTURE_DELETE)
-    {
-      SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting_rev,
-                                        parent_baton, scratch_pool));
-
-      /* No futher action possible for this node.  */
-      return SVN_NO_ERROR;
-    }
-
-  /* If we're not deleting this node, then we should know its kind.  */
-  SVN_ERR_ASSERT(change->kind != svn_node_unknown);
-
-#ifdef SHIM_WITH_ADD_ABSENT
-  if (change->action == RESTRUCTURE_ADD_ABSENT)
-    {
-      if (change->kind == svn_node_dir)
-        SVN_ERR(eb->deditor->absent_directory(ev1_relpath, parent_baton,
-                                              scratch_pool));
-      else if (change->kind == svn_node_file)
-        SVN_ERR(eb->deditor->absent_file(ev1_relpath, parent_baton,
-                                         scratch_pool));
-      else
-        SVN_ERR_MALFUNCTION();
-
-      /* No further action possible for this node.  */
-      return SVN_NO_ERROR;
-    }
-#endif
-  /* RESTRUCTURE_NONE or RESTRUCTURE_ADD  */
-
-  if (change->action == RESTRUCTURE_ADD)
-    {
-      const char *copyfrom_url = NULL;
-      svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
-
-      /* Do we have an old node to delete first? If so, delete it. */
-      if (change->deleting)
-        SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting_rev,
-                                          parent_baton, scratch_pool));
-
-      /* If it's a copy, determine the copy source location. */
-      if (change->copyfrom_path)
-        {
-          /* ### What's this about URL vs. fspath? REPOS_ROOT_URL isn't
-             optional, is it, at least in a commit editor? */
-          if (eb->repos_root_url)
-            copyfrom_url = svn_path_url_add_component2(eb->repos_root_url,
-                                                       change->copyfrom_path,
-                                                       scratch_pool);
-          else
-            {
-              copyfrom_url = change->copyfrom_path;
-
-              /* Make this an FS path by prepending "/" */
-              if (copyfrom_url[0] != '/')
-                copyfrom_url = apr_pstrcat(scratch_pool, "/",
-                                           copyfrom_url, SVN_VA_NULL);
-            }
-
-          copyfrom_rev = change->copyfrom_rev;
-        }
-
-      if (change->kind == svn_node_dir)
-        SVN_ERR(eb->deditor->add_directory(ev1_relpath, parent_baton,
-                                           copyfrom_url, copyfrom_rev,
-                                           result_pool, dir_baton));
-      else if (change->kind == svn_node_file)
-        SVN_ERR(eb->deditor->add_file(ev1_relpath, parent_baton,
-                                      copyfrom_url, copyfrom_rev,
-                                      result_pool, &file_baton));
-      else
-        SVN_ERR_MALFUNCTION();
-    }
-  else /* RESTRUCTURE_NONE */
-    {
-      /* ### The code that inserts a "plain edit" change record sets
-         'changing_rev' to the peg rev of the pegged part of the path,
-         even when the full path refers to a child of a copy. Should we
-         instead be using the copy source rev here, in that case? (Like
-         when we fetch the base properties.) */
-
-      if (change->kind == svn_node_dir)
-        SVN_ERR(eb->deditor->open_directory(ev1_relpath, parent_baton,
-                                            change->changing_rev,
-                                            result_pool, dir_baton));
-      else if (change->kind == svn_node_file)
-        SVN_ERR(eb->deditor->open_file(ev1_relpath, parent_baton,
-                                       change->changing_rev,
-                                       result_pool, &file_baton));
-      else
-        SVN_ERR_MALFUNCTION();
-    }
-
-  /* Apply any properties in CHANGE to the node.  */
-  if (change->kind == svn_node_dir)
-    SVN_ERR(drive_ev1_props(relpath, change, base_props,
-                            eb->deditor, *dir_baton, scratch_pool));
-  else
-    SVN_ERR(drive_ev1_props(relpath, change, base_props,
-                            eb->deditor, file_baton, scratch_pool));
-
-  /* Send the text content delta, if new text content is provided. */
-  if (change->contents_text)
-    {
-      svn_stream_t *read_stream;
-      svn_txdelta_window_handler_t handler;
-      void *handler_baton;
-
-      read_stream = svn_stream_from_stringbuf(change->contents_text,
-                                              scratch_pool);
-      /* ### would be nice to have a BASE_CHECKSUM, but hey: this is the
-         ### shim code...  */
-      SVN_ERR(eb->deditor->apply_textdelta(file_baton, NULL, scratch_pool,
-                                           &handler, &handler_baton));
-      /* ### it would be nice to send a true txdelta here, but whatever.  */
-      SVN_ERR(svn_txdelta_send_stream(read_stream, handler, handler_baton,
-                                      NULL, scratch_pool));
-      SVN_ERR(svn_stream_close(read_stream));
-    }
-
-  if (file_baton)
-    {
-      SVN_ERR(eb->deditor->close_file(file_baton, NULL, scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Drive the Ev1 with all the changes we have accumulated in EB.
- *
- * We visit each path operated on, and any ancestor directories, in an order

[... 1312 lines stripped ...]


Mime
View raw message