You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2015/10/26 12:06:51 UTC

svn commit: r1710565 [2/3] - in /subversion/branches/move-tracking-2/subversion/svnmover: merge3.c svnmover.c svnmover.h

Copied: subversion/branches/move-tracking-2/subversion/svnmover/merge3.c (from r1710534, subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c)
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svnmover/merge3.c?p2=subversion/branches/move-tracking-2/subversion/svnmover/merge3.c&p1=subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c&r1=1710534&r2=1710565&rev=1710565&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c (original)
+++ subversion/branches/move-tracking-2/subversion/svnmover/merge3.c Mon Oct 26 11:06:50 2015
@@ -1,5 +1,5 @@
 /*
- * svnmover.c: Concept Demo for Move Tracking and Branching
+ * merge3.c: 3-way merging
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -27,91 +27,25 @@
 
 #include <apr_lib.h>
 
-#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_iter.h"
 #include "svn_client.h"
-#include "svn_cmdline.h"
-#include "svn_config.h"
 #include "svn_error.h"
-#include "svn_path.h"
 #include "svn_pools.h"
 #include "svn_props.h"
 #include "svn_string.h"
-#include "svn_subst.h"
-#include "svn_utf.h"
-#include "svn_version.h"
-#include "svnmover.h"
 
-#include "private/svn_cmdline_private.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_branch_repos.h"
 #include "private/svn_branch_nested.h"
 #include "private/svn_branch_compat.h"
-#include "private/svn_ra_private.h"
-#include "private/svn_string_private.h"
 #include "private/svn_sorts_private.h"
-#include "private/svn_token.h"
 #include "private/svn_client_private.h"
-#include "../libsvn_delta/debug_editor.h"
-
-#define HAVE_LINENOISE
-#ifdef HAVE_LINENOISE
-#include "../libsvn_subr/linenoise/linenoise.h"
-#endif
-
-/* Version compatibility check */
-static svn_error_t *
-check_lib_versions(void)
-{
-  static const svn_version_checklist_t checklist[] =
-    {
-      { "svn_client", svn_client_version },
-      { "svn_subr",   svn_subr_version },
-      { "svn_ra",     svn_ra_version },
-      { NULL, NULL }
-    };
-  SVN_VERSION_DEFINE(my_version);
-
-  return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
-}
-
-static svn_boolean_t quiet = FALSE;
 
-/* UI mode: whether to display output in terms of paths or elements */
-enum { UI_MODE_EIDS, UI_MODE_PATHS, UI_MODE_SERIAL };
-static int the_ui_mode = UI_MODE_EIDS;
-static const svn_token_map_t ui_mode_map[]
-  = { {"eids", UI_MODE_EIDS},
-      {"e", UI_MODE_EIDS},
-      {"paths", UI_MODE_PATHS},
-      {"p", UI_MODE_PATHS},
-      {"serial", UI_MODE_SERIAL},
-      {"s", UI_MODE_SERIAL},
-      {NULL, SVN_TOKEN_UNKNOWN} };
-
-#define is_branch_root_element(branch, eid) \
-  (svn_branch_root_eid(branch) == (eid))
-
-/* Is BRANCH1 the same branch as BRANCH2? Compare by full branch-ids; don't
-   require identical branch objects. */
-#define BRANCH_IS_SAME_BRANCH(branch1, branch2, scratch_pool) \
-  (strcmp(svn_branch_get_id(branch1, scratch_pool), \
-          svn_branch_get_id(branch2, scratch_pool)) == 0)
+#include "svnmover.h"
 
-/* Print a notification. */
-__attribute__((format(printf, 1, 2)))
-static void
-notify(const char *fmt,
-       ...)
-{
-  va_list ap;
+#include "svn_private_config.h"
 
-  va_start(ap, fmt);
-  vprintf(fmt, ap);
-  va_end(ap);
-  printf("\n");
-}
 
 /* Print a verbose notification: in 'quiet' mode, don't print it. */
 __attribute__((format(printf, 1, 2)))
@@ -121,7 +55,7 @@ notify_v(const char *fmt,
 {
   va_list ap;
 
-  if (! quiet)
+  /*if (! quiet)*/
     {
       va_start(ap, fmt);
       vprintf(fmt, ap);
@@ -130,136 +64,19 @@ notify_v(const char *fmt,
     }
 }
 
-#define SVN_CL__LOG_SEP_STRING \
-  "------------------------------------------------------------------------\n"
-
-/* ====================================================================== */
-
-/* Update the WC to revision BASE_REVISION (SVN_INVALID_REVNUM means HEAD).
- *
- * Requires these fields in WC:
- *   head_revision
- *   repos_root_url
- *   ra_session
- *   pool
- *
- * Initializes these fields in WC:
- *   base_revision
- *   base_branch_id
- *   base_branch
- *   working_branch_id
- *   working_branch
- *   editor
- *
- * Assumes there are no changes in the WC: throws away the existing txn
- * and starts a new one.
- */
-static svn_error_t *
-wc_checkout(svnmover_wc_t *wc,
-            svn_revnum_t base_revision,
-            const char *base_branch_id,
-            apr_pool_t *scratch_pool)
+/*  */
+static int
+sort_compare_items_by_eid(const svn_sort__item_t *a,
+                          const svn_sort__item_t *b)
 {
-  const char *branch_info_dir = NULL;
-  svn_branch_compat__shim_fetch_func_t fetch_func;
-  void *fetch_baton;
-  svn_branch_txn_t *base_txn;
-
-  /* Validate and store the new base revision number */
-  if (! SVN_IS_VALID_REVNUM(base_revision))
-    base_revision = wc->head_revision;
-  else if (base_revision > wc->head_revision)
-    return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
-                             _("No such revision %ld (HEAD is %ld)"),
-                             base_revision, wc->head_revision);
-
-  /* Choose whether to store branching info in a local dir or in revprops.
-     (For now, just to exercise the options, we choose local files for
-     RA-local and revprops for a remote repo.) */
-  if (strncmp(wc->repos_root_url, "file://", 7) == 0)
-    {
-      const char *repos_dir;
-
-      SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dir, wc->repos_root_url,
-                                               scratch_pool));
-      branch_info_dir = svn_dirent_join(repos_dir, "branch-info", scratch_pool);
-    }
-
-  /* Get a mutable transaction based on that rev. (This implementation
-     re-reads all the move-tracking data from the repository.) */
-  SVN_ERR(svn_ra_load_branching_state(&wc->edit_txn,
-                                      &fetch_func, &fetch_baton,
-                                      wc->ra_session, branch_info_dir,
-                                      base_revision,
-                                      wc->pool, scratch_pool));
-
-  wc->edit_txn = svn_nested_branch_txn_create(wc->edit_txn, wc->pool);
-
-  /* Store the WC base state */
-  base_txn = svn_branch_repos_get_base_revision_root(wc->edit_txn);
-  wc->base = apr_pcalloc(wc->pool, sizeof(*wc->base));
-  wc->base->revision = base_revision;
-  wc->base->branch_id = apr_pstrdup(wc->pool, base_branch_id);
-  wc->base->branch
-    = svn_branch_txn_get_branch_by_id(base_txn, wc->base->branch_id,
-                                      scratch_pool);
-  if (! wc->base->branch)
-    return svn_error_createf(SVN_ERR_BRANCHING, NULL,
-                             "Cannot check out WC: branch %s not found in r%ld",
-                             base_branch_id, base_revision);
-
-  wc->working = apr_pcalloc(wc->pool, sizeof(*wc->working));
-  wc->working->revision = SVN_INVALID_REVNUM;
-  wc->working->branch_id = wc->base->branch_id;
-  wc->working->branch
-    = svn_branch_txn_get_branch_by_id(wc->edit_txn, wc->working->branch_id,
-                                      scratch_pool);
-  SVN_ERR_ASSERT(wc->working->branch);
+  int eid_a = *(const int *)a->key;
+  int eid_b = *(const int *)b->key;
 
-  return SVN_NO_ERROR;
+  return eid_a - eid_b;
 }
 
-/* Create a simulated WC, in memory.
- *
- * Initializes these fields in WC:
- *   head_revision
- *   repos_root_url
- *   ra_session
- *   made_changes
- *   ctx
- *   pool
- *
- * BASE_REVISION is the revision to work on, or SVN_INVALID_REVNUM for HEAD.
- */
-static svn_error_t *
-wc_create(svnmover_wc_t **wc_p,
-          const char *anchor_url,
-          svn_revnum_t base_revision,
-          const char *base_branch_id,
-          svn_client_ctx_t *ctx,
-          apr_pool_t *result_pool,
-          apr_pool_t *scratch_pool)
-{
-  apr_pool_t *wc_pool = svn_pool_create(result_pool);
-  svnmover_wc_t *wc = apr_pcalloc(wc_pool, sizeof(*wc));
-
-  wc->pool = wc_pool;
-  wc->ctx = ctx;
-
-  SVN_ERR(svn_client_open_ra_session2(&wc->ra_session, anchor_url,
-                                      NULL /* wri_abspath */, ctx,
-                                      wc_pool, scratch_pool));
-
-  SVN_ERR(svn_ra_get_repos_root2(wc->ra_session, &wc->repos_root_url,
-                                 result_pool));
-  SVN_ERR(svn_ra_get_latest_revnum(wc->ra_session, &wc->head_revision,
-                                   scratch_pool));
-  SVN_ERR(svn_ra_reparent(wc->ra_session, wc->repos_root_url, scratch_pool));
 
-  SVN_ERR(wc_checkout(wc, base_revision, base_branch_id, scratch_pool));
-  *wc_p = wc;
-  return SVN_NO_ERROR;
-}
+/* ====================================================================== */
 
 /* Return (left, right) pairs of element content that differ between
  * subtrees LEFT and RIGHT.
@@ -307,1185 +124,288 @@ element_differences(apr_hash_t **diff_p,
   return SVN_NO_ERROR;
 }
 
-/* Set *IS_CHANGED to true if EDIT_TXN differs from its base txn, else to
- * false.
+/* Return a string suitable for appending to a displayed element name or
+ * element id to indicate that it is a subbranch root element for SUBBRANCH.
+ * Return "" if SUBBRANCH is null.
  */
-static svn_error_t *
-txn_is_changed(svn_branch_txn_t *edit_txn,
-               svn_boolean_t *is_changed,
-               apr_pool_t *scratch_pool)
+static const char *
+branch_str(svn_branch_state_t *subbranch,
+           apr_pool_t *result_pool)
 {
-  SVN_ITER_T(svn_branch_state_t) *bi;
-  svn_branch_txn_t *base_txn
-    = svn_branch_repos_get_base_revision_root(edit_txn);
-  apr_array_header_t *edit_branches
-    = svn_branch_txn_get_branches(edit_txn, scratch_pool);
-  apr_array_header_t *base_branches
-    = svn_branch_txn_get_branches(base_txn, scratch_pool);
-
-  *is_changed = FALSE;
-
-  /* If any previous branch is now missing, that's a change. */
-  for (SVN_ARRAY_ITER(bi, base_branches, scratch_pool))
-    {
-      svn_branch_state_t *base_branch = bi->val;
-      svn_branch_state_t *edit_branch
-        = svn_branch_txn_get_branch_by_id(edit_txn, base_branch->bid,
-                                          scratch_pool);
-
-      if (! edit_branch)
-        {
-          *is_changed = TRUE;
-          return SVN_NO_ERROR;
-        }
-    }
-
-  /* If any current branch is new or changed, that's a change. */
-  for (SVN_ARRAY_ITER(bi, edit_branches, scratch_pool))
-    {
-      svn_branch_state_t *edit_branch = bi->val;
-      svn_branch_state_t *base_branch
-        = svn_branch_txn_get_branch_by_id(base_txn, edit_branch->bid,
-                                          scratch_pool);
-      apr_hash_t *diff;
-
-      if (! base_branch)
-        {
-          *is_changed = TRUE;
-          return SVN_NO_ERROR;
-        }
+  if (subbranch)
+    return apr_psprintf(result_pool,
+                      " (branch %s)",
+                      svn_branch_get_id(subbranch, result_pool));
+  return "";
+}
 
-      SVN_ERR(element_differences(&diff,
-                                  svn_branch_get_element_tree(edit_branch),
-                                  svn_branch_get_element_tree(base_branch),
-                                  scratch_pool, scratch_pool));
-      if (apr_hash_count(diff))
-        {
-          *is_changed = TRUE;
-          return SVN_NO_ERROR;
-        }
-    }
+/* Return a string suitable for appending to a displayed element name or
+ * element id to indicate that BRANCH:EID is a subbranch root element.
+ * Return "" if the element is not a subbranch root element.
+ */
+static const char *
+subbranch_str(svn_branch_state_t *branch,
+              int eid,
+              apr_pool_t *result_pool)
+{
+  svn_branch_state_t *subbranch
+    = svn_branch_get_subbranch_at_eid(branch, eid, result_pool);
 
-  return SVN_NO_ERROR;
+  return branch_str(subbranch, result_pool);
 }
 
-/* Replay differences between S_LEFT and S_RIGHT into EDITOR:EDIT_BRANCH.
+/* Options to control how strict the merge is about detecting conflicts.
  *
- * S_LEFT and/or S_RIGHT may be null meaning an empty set.
+ * The options affect cases that, depending on the user's preference, could
+ * either be considered a conflict or be merged to a deterministic result.
  *
- * Non-recursive: single branch only.
+ * The set of options is flexible and may be extended in future.
  */
-static svn_error_t *
-subtree_replay(svn_branch_state_t *edit_branch,
-               const svn_element_tree_t *s_left,
-               const svn_element_tree_t *s_right,
-               apr_pool_t *scratch_pool)
+typedef struct merge_conflict_policy_t
 {
-  apr_hash_t *diff_left_right;
-  apr_hash_index_t *hi;
-
-  if (! s_left)
-    s_left = svn_element_tree_create(NULL, 0 /*root_eid*/, scratch_pool);
-  if (! s_right)
-    s_right = svn_element_tree_create(NULL, 0 /*root_eid*/, scratch_pool);
-
-  SVN_ERR(element_differences(&diff_left_right,
-                              s_left, s_right,
-                              scratch_pool, scratch_pool));
-
-  /* Go through the per-element differences. */
-  for (hi = apr_hash_first(scratch_pool, diff_left_right);
-       hi; hi = apr_hash_next(hi))
-    {
-      int eid = svn_int_hash_this_key(hi);
-      svn_element_content_t **e_pair = apr_hash_this_val(hi);
-      svn_element_content_t *e0 = e_pair[0], *e1 = e_pair[1];
-
-      SVN_ERR_ASSERT(!e0
-                     || svn_element_payload_invariants(e0->payload));
-      SVN_ERR_ASSERT(!e1
-                     || svn_element_payload_invariants(e1->payload));
-      if (e0 || e1)
-        {
-          if (e0 && e1)
-            {
-              SVN_DBG(("replay: alter e%d", eid));
-              SVN_ERR(svn_branch_state_alter_one(edit_branch, eid,
-                                                 e1->parent_eid, e1->name,
-                                                 e1->payload, scratch_pool));
-            }
-          else if (e0)
-            {
-              SVN_DBG(("replay: delete e%d", eid));
-              SVN_ERR(svn_branch_state_delete_one(edit_branch, eid,
-                                                  scratch_pool));
-            }
-          else
-            {
-              SVN_DBG(("replay: instan. e%d", eid));
-              SVN_ERR(svn_branch_state_alter_one(edit_branch, eid,
-                                                 e1->parent_eid, e1->name,
-                                                 e1->payload, scratch_pool));
-            }
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
+  /* Whether to merge delete-vs-delete */
+  svn_boolean_t merge_double_delete;
+  /* Whether to merge add-vs-add (with same parent/name/payload) */
+  svn_boolean_t merge_double_add;
+  /* Whether to merge reparent-vs-reparent (with same parent) */
+  svn_boolean_t merge_double_reparent;
+  /* Whether to merge rename-vs-rename (with same name) */
+  svn_boolean_t merge_double_rename;
+  /* Whether to merge modify-vs-modify (with same payload) */
+  svn_boolean_t merge_double_modify;
+  /* Possible additional controls: */
+  /* merge (parent, name, props, text) independently or as a group */
+  /* merge (parent, name) independently or as a group */
+  /* merge (props, text) independently or as a group */
+} merge_conflict_policy_t;
 
-/*  */
-static apr_hash_t *
-get_union_of_subbranches(svn_branch_state_t *left_branch,
-                         svn_branch_state_t *right_branch,
-                         apr_pool_t *result_pool)
+/* An element-merge conflict description.
+ */
+typedef struct element_merge3_conflict_t
 {
-  apr_hash_t *all_subbranches;
+  svn_element_content_t *yca;
+  svn_element_content_t *side1;
+  svn_element_content_t *side2;
+} element_merge3_conflict_t;
 
-  svn_branch_subtree_t *s_left
-    = left_branch ? svn_branch_get_subtree(left_branch,
-                                           svn_branch_root_eid(left_branch),
-                                           result_pool) : NULL;
-  svn_branch_subtree_t *s_right
-    = right_branch ? svn_branch_get_subtree(right_branch,
-                                            svn_branch_root_eid(right_branch),
-                                            result_pool) : NULL;
-  all_subbranches
-    = left_branch ? apr_hash_overlay(result_pool,
-                                     s_left->subbranches, s_right->subbranches)
-                  : s_right->subbranches;
+static element_merge3_conflict_t *
+element_merge3_conflict_create(svn_element_content_t *yca,
+                               svn_element_content_t *side1,
+                               svn_element_content_t *side2,
+                               apr_pool_t *result_pool)
+{
+  element_merge3_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
 
-  return all_subbranches;
+  c->yca = yca;
+  c->side1 = side1;
+  c->side2 = side2;
+  return c;
 }
 
-/* Replay differences between S_LEFT and S_RIGHT into EDITOR:EDIT_BRANCH.
- *
- * S_LEFT or S_RIGHT (but not both) may be null meaning an empty set.
- *
- * Recurse into subbranches.
+/* A name-clash conflict description.
  */
-static svn_error_t *
-svn_branch_replay(svn_branch_txn_t *edit_txn,
-                  svn_branch_state_t *edit_branch,
-                  svn_branch_state_t *left_branch,
-                  svn_branch_state_t *right_branch,
-                  apr_pool_t *scratch_pool)
+typedef struct name_clash_conflict_t
 {
-  assert((left_branch && right_branch)
-         ? (svn_branch_root_eid(left_branch) == svn_branch_root_eid(right_branch))
-         : (left_branch || right_branch));
-
-  if (right_branch)
-    {
-      /* Replay this branch */
-      const svn_element_tree_t *s_left
-        = left_branch ? svn_branch_get_element_tree(left_branch) : NULL;
-      const svn_element_tree_t *s_right
-        = right_branch ? svn_branch_get_element_tree(right_branch) : NULL;
-
-      SVN_ERR(subtree_replay(edit_branch, s_left, s_right,
-                             scratch_pool));
-    }
-  else
-    {
-      /* deleted branch LEFT */
-      /* nothing to do -- it will go away because we deleted the outer-branch
-         element where it was attached */
-    }
-
-  /* Replay its subbranches, recursively.
-     (If we're deleting the current branch, we don't also need to
-     explicitly delete its subbranches... do we?) */
-  if (right_branch)
-    {
-      apr_hash_t *all_subbranches
-        = get_union_of_subbranches(left_branch, right_branch, scratch_pool);
-      apr_hash_index_t *hi;
-
-      for (hi = apr_hash_first(scratch_pool, all_subbranches);
-           hi; hi = apr_hash_next(hi))
-        {
-          int this_eid = svn_int_hash_this_key(hi);
-          svn_branch_state_t *left_subbranch
-            = left_branch ? svn_branch_get_subbranch_at_eid(
-                              left_branch, this_eid, scratch_pool) : NULL;
-          svn_branch_state_t *right_subbranch
-            = right_branch ? svn_branch_get_subbranch_at_eid(
-                               right_branch, this_eid, scratch_pool) : NULL;
-          svn_branch_state_t *edit_subbranch = NULL;
-
-          /* If the subbranch is to be edited or added, first look up the
-             corresponding edit subbranch, or, if not found, create one. */
-          if (right_subbranch)
-            {
-              const char *new_branch_id
-                = svn_branch_id_nest(edit_branch->bid, this_eid, scratch_pool);
-
-              SVN_ERR(svn_branch_txn_open_branch(edit_txn, &edit_subbranch,
-                                                 right_subbranch->predecessor,
-                                                 new_branch_id,
-                                                 svn_branch_root_eid(right_subbranch),
-                                                 scratch_pool, scratch_pool));
-            }
+  int parent_eid;
+  const char *name;
+  /* All EIDs that conflict with each other: hash of (eid -> irrelevant). */
+  apr_hash_t *elements;
+} name_clash_conflict_t;
 
-          /* recurse */
-          if (edit_subbranch)
-            {
-              SVN_ERR(svn_branch_replay(edit_txn, edit_subbranch,
-                                        left_subbranch, right_subbranch,
-                                        scratch_pool));
-            }
-        }
-    }
+static name_clash_conflict_t *
+name_clash_conflict_create(int parent_eid,
+                           const char *name,
+                           apr_pool_t *result_pool)
+{
+  name_clash_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
 
-  return SVN_NO_ERROR;
+  c->parent_eid = parent_eid;
+  c->name = apr_pstrdup(result_pool, name);
+  c->elements = apr_hash_make(result_pool);
+  return c;
 }
 
-/* Replay differences between LEFT_BRANCH and RIGHT_BRANCH into
- * EDIT_ROOT_BRANCH.
- * (Recurse into subbranches.)
+/* An orphan conflict description.
  */
-static svn_error_t *
-replay(svn_branch_txn_t *edit_txn,
-       svn_branch_state_t *edit_root_branch,
-       svn_branch_state_t *left_branch,
-       svn_branch_state_t *right_branch,
-       apr_pool_t *scratch_pool)
+typedef struct orphan_conflict_t
 {
-  SVN_ERR_ASSERT(left_branch || right_branch);
+  svn_element_content_t *element;
+} orphan_conflict_t;
 
-  SVN_ERR(svn_branch_replay(edit_txn, edit_root_branch,
-                            left_branch, right_branch, scratch_pool));
-  return SVN_NO_ERROR;
-}
+static orphan_conflict_t *
+orphan_conflict_create(svn_element_content_t *element,
+                       apr_pool_t *result_pool)
+{
+  orphan_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
 
-static svn_error_t *
-commit_callback(const svn_commit_info_t *commit_info,
-                void *baton,
-                apr_pool_t *pool);
+  c->element = element;
+  return c;
+}
 
-/* Baton for commit_callback(). */
-typedef struct commit_callback_baton_t
+/*  */
+static conflict_storage_t *
+conflict_storage_create(apr_pool_t *result_pool)
 {
-  svn_branch_txn_t *edit_txn;
-  const char *wc_base_branch_id;
-  const char *wc_commit_branch_id;
-
-  /* just-committed revision */
-  svn_revnum_t revision;
-} commit_callback_baton_t;
-
-static svn_error_t *
-display_diff_of_commit(const commit_callback_baton_t *ccbb,
-                       apr_pool_t *scratch_pool);
+  conflict_storage_t *c = apr_pcalloc(result_pool, sizeof(*c));
 
-static svn_error_t *
-do_topbranch(svn_branch_state_t **new_branch_p,
-             svn_branch_txn_t *txn,
-             svn_branch_rev_bid_eid_t *from,
-             apr_pool_t *result_pool,
-             apr_pool_t *scratch_pool);
+  return c;
+}
 
-/* Allocate the same number of new EIDs in NEW_TXN as are already
- * allocated in OLD_TXN.
- */
-static svn_error_t *
-allocate_eids(svn_branch_txn_t *new_txn,
-              const svn_branch_txn_t *old_txn,
-              apr_pool_t *scratch_pool)
+/*  */
+static const char *
+brief_eid_and_name_or_nil(svn_element_content_t *e,
+                          apr_pool_t *result_pool)
 {
-  int num_new_eids;
-  int i;
-
-  SVN_ERR(svn_branch_txn_get_num_new_eids(old_txn, &num_new_eids,
-                                          scratch_pool));
-  for (i = 0; i < num_new_eids; i++)
-    {
-      SVN_ERR(svn_branch_txn_new_eid(new_txn, NULL, scratch_pool));
-    }
+  return e ? apr_psprintf(result_pool, "%d/%s", e->parent_eid, e->name)
+           : "<nil>";
 
   return SVN_NO_ERROR;
 }
 
-/* Commit the changes from WC into the repository.
- *
- * Open a new commit txn to the repo. Replay the changes from WC into it.
- *
- * Set WC->head_revision and *NEW_REV_P to the committed revision number.
- *
- * If there are no changes to commit, set *NEW_REV_P to SVN_INVALID_REVNUM
- * and do not make a commit and do not change WC->head_revision.
- *
- * NEW_REV_P may be null if not wanted.
- */
-static svn_error_t *
-wc_commit(svn_revnum_t *new_rev_p,
-          svnmover_wc_t *wc,
-          apr_hash_t *revprops,
-          apr_pool_t *scratch_pool)
+svn_error_t *
+svnmover_display_conflicts(conflict_storage_t *conflict_storage,
+                           const char *prefix,
+                           apr_pool_t *scratch_pool)
 {
-  const char *branch_info_dir = NULL;
-  svn_branch_txn_t *commit_txn;
-  commit_callback_baton_t ccbb;
-  svn_boolean_t change_detected;
-  const char *edit_root_branch_id;
-  svn_branch_state_t *edit_root_branch;
+  apr_hash_index_t *hi;
 
-  /* If no log msg provided, use the list of commands */
-  if (! svn_hash_gets(revprops, SVN_PROP_REVISION_LOG) && wc->list_of_commands)
+  for (hi = apr_hash_first(scratch_pool,
+                           conflict_storage->single_element_conflicts);
+       hi; hi = apr_hash_next(hi))
     {
-      /* Avoid modifying the passed-in revprops hash */
-      revprops = apr_hash_copy(scratch_pool, revprops);
+      int eid = svn_int_hash_this_key(hi);
+      element_merge3_conflict_t *c = apr_hash_this_val(hi);
 
-      svn_hash_sets(revprops, SVN_PROP_REVISION_LOG,
-                    svn_string_create(wc->list_of_commands, scratch_pool));
+      printf("%ssingle-element conflict: e%d: yca=%s, side1=%s, side2=%s\n",
+             prefix, eid,
+             brief_eid_and_name_or_nil(c->yca, scratch_pool),
+             brief_eid_and_name_or_nil(c->side1, scratch_pool),
+             brief_eid_and_name_or_nil(c->side2, scratch_pool));
     }
-
-  /* Choose whether to store branching info in a local dir or in revprops.
-     (For now, just to exercise the options, we choose local files for
-     RA-local and revprops for a remote repo.) */
-  if (strncmp(wc->repos_root_url, "file://", 7) == 0)
+  for (hi = apr_hash_first(scratch_pool,
+                           conflict_storage->name_clash_conflicts);
+       hi; hi = apr_hash_next(hi))
     {
-      const char *repos_dir;
-
-      SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dir, wc->repos_root_url,
-                                               scratch_pool));
-      branch_info_dir = svn_dirent_join(repos_dir, "branch-info", scratch_pool);
-    }
+      /*const char *key = apr_hash_this_key(hi);*/
+      name_clash_conflict_t *c = apr_hash_this_val(hi);
+      apr_hash_index_t *hi2;
 
-  /* Start a new editor for the commit. */
-  SVN_ERR(svn_ra_get_commit_txn(wc->ra_session,
-                                &commit_txn,
-                                revprops,
-                                commit_callback, &ccbb,
-                                NULL /*lock_tokens*/, FALSE /*keep_locks*/,
-                                branch_info_dir,
-                                scratch_pool));
-  /*SVN_ERR(svn_branch_txn__get_debug(&wc->edit_txn, wc->edit_txn, scratch_pool));*/
-
-  edit_root_branch_id = wc->working->branch_id;
-  edit_root_branch = svn_branch_txn_get_branch_by_id(
-                       commit_txn, wc->working->branch_id, scratch_pool);
-
-  /* We might be creating a new top-level branch in this commit. That is the
-     only case in which the working branch will not be found in EDIT_TXN.
-     (Creating any other branch can only be done inside a checkout of a
-     parent branch.) So, maybe create a new top-level branch. */
-  if (! edit_root_branch)
-    {
-      /* Create a new top-level branch in the edited state. (It will have
-         an independent new top-level branch number.) */
-      svn_branch_rev_bid_eid_t *from
-        = svn_branch_rev_bid_eid_create(wc->base->revision, wc->base->branch_id,
-                                        svn_branch_root_eid(wc->base->branch),
-                                        scratch_pool);
+      printf("%sname-clash conflict: peid %d, name '%s', %d elements\n",
+             prefix, c->parent_eid, c->name, apr_hash_count(c->elements));
+      for (hi2 = apr_hash_first(scratch_pool, c->elements);
+           hi2; hi2 = apr_hash_next(hi2))
+        {
+          int eid = svn_int_hash_this_key(hi2);
 
-      SVN_ERR(do_topbranch(&edit_root_branch, commit_txn,
-                           from, scratch_pool, scratch_pool));
-      edit_root_branch_id = edit_root_branch->bid;
-    }
-  /* Allocate all the new eids we'll need in this new txn */
-  SVN_ERR(allocate_eids(commit_txn, wc->working->branch->txn, scratch_pool));
-  SVN_ERR(replay(commit_txn, edit_root_branch,
-                 wc->base->branch,
-                 wc->working->branch,
-                 scratch_pool));
-  SVN_ERR(txn_is_changed(commit_txn, &change_detected,
-                         scratch_pool));
-  if (change_detected)
-    {
-      ccbb.edit_txn = commit_txn;
-      ccbb.wc_base_branch_id = wc->base->branch_id;
-      ccbb.wc_commit_branch_id = edit_root_branch_id;
-
-      SVN_ERR(svn_branch_txn_complete(commit_txn, scratch_pool));
-      SVN_ERR(display_diff_of_commit(&ccbb, scratch_pool));
-
-      wc->head_revision = ccbb.revision;
-      if (new_rev_p)
-        *new_rev_p = ccbb.revision;
+          printf("%s  element %d\n", prefix, eid);
+        }
     }
-  else
+  for (hi = apr_hash_first(scratch_pool,
+                           conflict_storage->orphan_conflicts);
+       hi; hi = apr_hash_next(hi))
     {
-      SVN_ERR(svn_branch_txn_abort(commit_txn, scratch_pool));
-      if (new_rev_p)
-        *new_rev_p = SVN_INVALID_REVNUM;
-    }
-
-  wc->list_of_commands = NULL;
+      int eid = svn_int_hash_this_key(hi);
+      orphan_conflict_t *c = apr_hash_this_val(hi);
 
+      printf("%sorphan conflict: element %d/%s: peid %d does not exist\n",
+             prefix, eid, c->element->name, c->element->parent_eid);
+    }
   return SVN_NO_ERROR;
 }
 
-typedef enum action_code_t {
-  ACTION_INFO_WC,
-  ACTION_DIFF,
-  ACTION_LOG,
-  ACTION_LIST_BRANCHES,
-  ACTION_LIST_BRANCHES_R,
-  ACTION_LS,
-  ACTION_TBRANCH,
-  ACTION_BRANCH,
-  ACTION_BRANCH_INTO,
-  ACTION_MKBRANCH,
-  ACTION_MERGE,
-  ACTION_MV,
-  ACTION_MKDIR,
-  ACTION_PUT_FILE,
-  ACTION_CAT,
-  ACTION_CP,
-  ACTION_RM,
-  ACTION_CP_RM,
-  ACTION_BR_RM,
-  ACTION_BR_INTO_RM,
-  ACTION_COMMIT,
-  ACTION_UPDATE,
-  ACTION_SWITCH,
-  ACTION_STATUS,
-  ACTION_REVERT,
-  ACTION_MIGRATE
-} action_code_t;
-
-typedef struct action_defn_t {
-  enum action_code_t code;
-  const char *name;
-  int num_args;
-  const char *args_help;
-  const char *help;
-} action_defn_t;
-
-#define NL "\n                           "
-static const action_defn_t action_defn[] =
-{
-  {ACTION_INFO_WC,          "info-wc", 0, "",
-    "print information about the WC"},
-  {ACTION_LIST_BRANCHES,    "branches", 1, "PATH",
-    "list all branches rooted at the same element as PATH"},
-  {ACTION_LIST_BRANCHES_R,  "ls-br-r", 0, "",
-    "list all branches, recursively"},
-  {ACTION_LS,               "ls", 1, "PATH",
-    "list elements in the branch found at PATH"},
-  {ACTION_LOG,              "log", 2, "FROM@REV TO@REV",
-    "show per-revision diffs between FROM and TO"},
-  {ACTION_TBRANCH,          "tbranch", 1, "SRC",
-    "branch the branch-root or branch-subtree at SRC" NL
-    "to make a new top-level branch"},
-  {ACTION_BRANCH,           "branch", 2, "SRC DST",
-    "branch the branch-root or branch-subtree at SRC" NL
-    "to make a new branch at DST"},
-  {ACTION_BRANCH_INTO,      "branch-into", 2, "SRC DST",
-    "make a branch of the existing subtree SRC appear at" NL
-    "DST as part of the existing branch that contains DST" NL
-    "(like merging the creation of SRC to DST)"},
-  {ACTION_MKBRANCH,         "mkbranch", 1, "ROOT",
-    "make a directory that's the root of a new subbranch"},
-  {ACTION_DIFF,             "diff", 2, "LEFT@REV RIGHT@REV",
-    "show differences from subtree LEFT to subtree RIGHT"},
-  {ACTION_MERGE,            "merge", 3, "FROM TO YCA@REV",
-    "3-way merge YCA->FROM into TO"},
-  {ACTION_CP,               "cp", 2, "REV SRC DST",
-    "copy SRC@REV to DST"},
-  {ACTION_MV,               "mv", 2, "SRC DST",
-    "move SRC to DST"},
-  {ACTION_RM,               "rm", 1, "PATH",
-    "delete PATH"},
-  {ACTION_CP_RM,            "copy-and-delete", 2, "SRC DST",
-    "copy-and-delete SRC to DST"},
-  {ACTION_BR_RM,            "branch-and-delete", 2, "SRC DST",
-    "branch-and-delete SRC to DST"},
-  {ACTION_BR_INTO_RM,       "branch-into-and-delete", 2, "SRC DST",
-    "merge-and-delete SRC to DST"},
-  {ACTION_MKDIR,            "mkdir", 1, "PATH",
-    "create new directory PATH"},
-  {ACTION_PUT_FILE,         "put", 2, "LOCAL_FILE PATH",
-    "add or modify file PATH with text copied from" NL
-    "LOCAL_FILE (use \"-\" to read from standard input)"},
-  {ACTION_CAT,              "cat", 1, "PATH",
-    "display text (for a file) and props (if any) of PATH"},
-  {ACTION_COMMIT,           "commit", 0, "",
-    "commit the changes"},
-  {ACTION_UPDATE,           "update", 1, ".@REV",
-    "update to revision REV, keeping local changes"},
-  {ACTION_SWITCH,           "switch", 1, "TARGET[@REV]",
-    "switch to another branch and/or revision, keeping local changes"},
-  {ACTION_STATUS,           "status", 0, "",
-    "same as 'diff .@base .'"},
-  {ACTION_REVERT,           "revert", 0, "",
-    "revert all uncommitted changes"},
-  {ACTION_MIGRATE,          "migrate", 1, ".@REV",
-    "migrate changes from non-move-tracking revision"},
-};
-
-typedef struct action_t {
-  /* The original command words (const char *) by which the action was
-     specified */
-  apr_array_header_t *action_args;
-
-  action_code_t action;
-
-  /* argument revisions */
-  svn_opt_revision_t rev_spec[3];
-
-  const char *branch_id[3];
-
-  /* argument paths */
-  const char *relpath[3];
-} action_t;
-
-/* ====================================================================== */
-
-/* Find the deepest branch in the repository of which REVNUM:BRANCH_ID:RELPATH
- * is either the root element or a normal, non-sub-branch element.
- *
- * RELPATH is a repository-relative path. REVNUM is a revision number, or
- * SVN_INVALID_REVNUM meaning the current txn.
+/* Merge the payload for one element.
  *
- * Return the location of the element in that branch, or with
- * EID=-1 if no element exists there.
+ * If there is no conflict, set *CONFLICT_P to FALSE and *RESULT_P to the
+ * merged element; otherwise set *CONFLICT_P to TRUE and *RESULT_P to NULL.
+ * Note that *RESULT_P can be null, indicating a deletion.
  *
- * If BRANCH_ID is null, the default is the WC base branch when REVNUM is
- * specified, and the WC working branch when REVNUM is SVN_INVALID_REVNUM.
+ * This handles any case where at least one of (SIDE1, SIDE2, YCA) exists.
  *
- * Return an error if branch BRANCH_ID does not exist in r<REVNUM>; otherwise,
- * the result will never be NULL, as every path is within at least the root
- * branch.
- */
-static svn_error_t *
-find_el_rev_by_rrpath_rev(svn_branch_el_rev_id_t **el_rev_p,
-                          svnmover_wc_t *wc,
-                          svn_revnum_t revnum,
-                          const char *branch_id,
-                          const char *relpath,
-                          apr_pool_t *result_pool,
-                          apr_pool_t *scratch_pool)
-{
-  if (SVN_IS_VALID_REVNUM(revnum))
-    {
-      const svn_branch_repos_t *repos = wc->working->branch->txn->repos;
-
-      if (! branch_id)
-        branch_id = wc->base->branch_id;
-      SVN_ERR(svn_branch_repos_find_el_rev_by_path_rev(el_rev_p, repos,
-                                                       revnum,
-                                                       branch_id,
-                                                       relpath,
-                                                       result_pool,
-                                                       scratch_pool));
-    }
-  else
-    {
-      svn_branch_state_t *branch
-        = branch_id ? svn_branch_txn_get_branch_by_id(
-                        wc->working->branch->txn, branch_id, scratch_pool)
-                    : wc->working->branch;
-      svn_branch_el_rev_id_t *el_rev = apr_palloc(result_pool, sizeof(*el_rev));
-
-      if (! branch)
-        return svn_error_createf(SVN_ERR_BRANCHING, NULL,
-                                 _("Branch %s not found in working state"),
-                                 branch_id);
-      svn_branch_find_nested_branch_element_by_relpath(
-        &el_rev->branch, &el_rev->eid,
-        branch, relpath, scratch_pool);
-      el_rev->rev = SVN_INVALID_REVNUM;
-      *el_rev_p = el_rev;
-    }
-  SVN_ERR_ASSERT(*el_rev_p);
-  return SVN_NO_ERROR;
-}
-
-/* Return a string suitable for appending to a displayed element name or
- * element id to indicate that it is a subbranch root element for SUBBRANCH.
- * Return "" if SUBBRANCH is null.
- */
-static const char *
-branch_str(svn_branch_state_t *subbranch,
-           apr_pool_t *result_pool)
-{
-  if (subbranch)
-    return apr_psprintf(result_pool,
-                      " (branch %s)",
-                      svn_branch_get_id(subbranch, result_pool));
-  return "";
-}
-
-/* Return a string suitable for appending to a displayed element name or
- * element id to indicate that BRANCH:EID is a subbranch root element.
- * Return "" if the element is not a subbranch root element.
+ * Allocate the result in RESULT_POOL and/or as pointers to the inputs.
  */
-static const char *
-subbranch_str(svn_branch_state_t *branch,
+static void
+payload_merge(svn_element_payload_t **result_p,
+              svn_boolean_t *conflict_p,
               int eid,
-              apr_pool_t *result_pool)
-{
-  svn_branch_state_t *subbranch
-    = svn_branch_get_subbranch_at_eid(branch, eid, result_pool);
-
-  return branch_str(subbranch, result_pool);
-}
-
-/*  */
-static const char *
-subtree_subbranch_str(svn_branch_subtree_t *subtree,
-                      const char *bid,
-                      int eid,
-                      apr_pool_t *result_pool)
-{
-  svn_branch_subtree_t *subbranch
-    = svn_branch_subtree_get_subbranch_at_eid(subtree, eid, result_pool);
-
-  if (subbranch)
-    return apr_psprintf(result_pool,
-                        " (branch %s)",
-                        svn_branch_id_nest(bid, eid, result_pool));
-  return "";
-}
-
-/*  */
-static const char *
-el_rev_id_to_path(svn_branch_el_rev_id_t *el_rev,
-                  apr_pool_t *result_pool)
-{
-  const char *path
-    = svn_branch_get_rrpath_by_eid(el_rev->branch, el_rev->eid, result_pool);
-
-  return path;
-}
-
-/*  */
-static const char *
-branch_peid_name_to_path(svn_branch_state_t *to_branch,
-                         int to_parent_eid,
-                         const char *to_name,
-                         apr_pool_t *result_pool)
-{
-  const char *path
-    = svn_relpath_join(svn_branch_get_rrpath_by_eid(to_branch, to_parent_eid,
-                                                    result_pool),
-                       to_name, result_pool);
-
-  return path;
-}
-
-/* List the elements in BRANCH, in path notation.
- *
- * List only the elements for which a relpath is known -- that is, elements
- * whose parents exist all the way up to the branch root.
- */
-static svn_error_t *
-list_branch_elements(svn_branch_state_t *branch,
-                     apr_pool_t *scratch_pool)
+              svn_element_payload_t *side1,
+              svn_element_payload_t *side2,
+              svn_element_payload_t *yca,
+              const merge_conflict_policy_t *policy,
+              apr_pool_t *result_pool,
+              apr_pool_t *scratch_pool)
 {
-  apr_hash_t *paths_to_eid = apr_hash_make(scratch_pool);
-  apr_hash_index_t *hi;
-  SVN_ITER_T(int) *pi;
+  svn_boolean_t conflict = FALSE;
+  svn_element_payload_t *result = NULL;
 
-  for (hi = apr_hash_first(scratch_pool, svn_branch_get_elements(branch));
-       hi; hi = apr_hash_next(hi))
+  if (yca && side1 && side2)
     {
-      int eid = svn_int_hash_this_key(hi);
-      const char *relpath = svn_branch_get_path_by_eid(branch, eid,
-                                                       scratch_pool);
-
-      if (relpath)
+      if (svn_element_payload_equal(side1, yca, scratch_pool))
         {
-          svn_hash_sets(paths_to_eid, relpath, apr_pmemdup(scratch_pool,
-                                                           &eid, sizeof(eid)));
+          result = side2;
         }
-    }
-  for (SVN_HASH_ITER_SORTED(pi, paths_to_eid, svn_sort_compare_items_as_paths, scratch_pool))
-    {
-      const char *relpath = pi->key;
-      int eid = *pi->val;
-
-      notify("    %-20s%s",
-             relpath[0] ? relpath : ".",
-             subbranch_str(branch, eid, scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*  */
-static int
-sort_compare_items_by_eid(const svn_sort__item_t *a,
-                          const svn_sort__item_t *b)
-{
-  int eid_a = *(const int *)a->key;
-  int eid_b = *(const int *)b->key;
-
-  return eid_a - eid_b;
-}
-
-static const char *
-peid_name(const svn_element_content_t *element,
-          apr_pool_t *scratch_pool)
-{
-  if (element->parent_eid == -1)
-    return apr_psprintf(scratch_pool, "%3s %-10s", "", ".");
-
-  return apr_psprintf(scratch_pool, "%3d/%-10s",
-                      element->parent_eid, element->name);
-}
-
-static const char elements_by_eid_header[]
-  = "    eid  parent-eid/name\n"
-    "    ---  ----------/----";
-
-/* List all elements in branch BRANCH, in element notation.
- */
-static svn_error_t *
-list_branch_elements_by_eid(svn_branch_state_t *branch,
-                            apr_pool_t *scratch_pool)
-{
-  SVN_ITER_T(svn_element_content_t) *pi;
-
-  notify_v("%s", elements_by_eid_header);
-  for (SVN_HASH_ITER_SORTED(pi, svn_branch_get_elements(branch),
-                            sort_compare_items_by_eid, scratch_pool))
-    {
-      int eid = *(const int *)(pi->key);
-      svn_element_content_t *element = pi->val;
-
-      if (element)
+      else if (svn_element_payload_equal(side2, yca, scratch_pool))
         {
-          notify("    e%-3d %21s%s",
-                 eid,
-                 peid_name(element, scratch_pool),
-                 subbranch_str(branch, eid, scratch_pool));
+          result = side1;
         }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*  */
-static const char *
-branch_id_header_str(const char *prefix,
-                     apr_pool_t *result_pool)
-{
-  if (the_ui_mode == UI_MODE_PATHS)
-    {
-      return apr_psprintf(result_pool,
-                          "%sbranch-id  root-path\n"
-                          "%s---------  ---------",
-                          prefix, prefix);
-    }
-  else
-    {
-      return apr_psprintf(result_pool,
-                          "%sbranch-id  branch-name  root-eid\n"
-                          "%s---------  -----------  --------",
-                          prefix, prefix);
-    }
-}
-
-/* Show the id and path or root-eid of BRANCH.
- */
-static const char *
-branch_id_str(svn_branch_state_t *branch,
-              apr_pool_t *result_pool)
-{
-  apr_pool_t *scratch_pool = result_pool;
-
-  if (the_ui_mode == UI_MODE_PATHS)
-    {
-      return apr_psprintf(result_pool, "%-10s /%s",
-                          svn_branch_get_id(branch, scratch_pool),
-                          svn_branch_get_root_rrpath(branch, scratch_pool));
-    }
-  else
-    {
-      svn_element_content_t *outer_el = NULL;
-      svn_branch_state_t *outer_branch;
-      int outer_eid;
-
-      svn_branch_get_outer_branch_and_eid(&outer_branch, &outer_eid,
-                                          branch, scratch_pool);
-
-      if (outer_branch)
-        outer_el = svn_branch_get_element(outer_branch, outer_eid);
-
-      return apr_psprintf(result_pool, "%-10s %-12s root=e%d",
-                          svn_branch_get_id(branch, scratch_pool),
-                          outer_el ? outer_el->name : "/",
-                          svn_branch_root_eid(branch));
-    }
-}
-
-/* List the branch BRANCH.
- *
- * If WITH_ELEMENTS is true, also list the elements in it.
- */
-static svn_error_t *
-list_branch(svn_branch_state_t *branch,
-            svn_boolean_t with_elements,
-            apr_pool_t *scratch_pool)
-{
-  notify_v("  %s", branch_id_str(branch, scratch_pool));
-
-  if (with_elements)
-    {
-      if (the_ui_mode == UI_MODE_PATHS)
+      else if (policy->merge_double_modify
+               && svn_element_payload_equal(side1, side2, scratch_pool))
         {
-          SVN_ERR(list_branch_elements(branch, scratch_pool));
+          SVN_DBG(("e%d double modify: ... -> { ... | ... }",
+                   eid));
+          result = side1;
         }
       else
         {
-          SVN_ERR(list_branch_elements_by_eid(branch, scratch_pool));
+          /* ### Need not conflict if can merge props and text separately. */
+
+          SVN_DBG(("e%d conflict: payload: ... -> { ... | ... }",
+                   eid));
+          conflict = TRUE;
         }
     }
-  return SVN_NO_ERROR;
+
+  *result_p = result;
+  *conflict_p = conflict;
 }
 
-/* List all branches rooted at EID.
+/* Merge the content for one element.
+ *
+ * If there is no conflict, set *CONFLICT_P to FALSE and *RESULT_P to the
+ * merged element; otherwise set *CONFLICT_P to TRUE and *RESULT_P to NULL.
+ * Note that *RESULT_P can be null, indicating a deletion.
+ *
+ * This handles any case where at least one of (SIDE1, SIDE2, YCA) exists.
  *
- * If WITH_ELEMENTS is true, also list the elements in each branch.
+ * Allocate the result in RESULT_POOL and/or as pointers to the inputs.
  */
-static svn_error_t *
-list_branches(svn_branch_txn_t *txn,
+static void
+element_merge(svn_element_content_t **result_p,
+              element_merge3_conflict_t **conflict_p,
               int eid,
-              svn_boolean_t with_elements,
+              svn_element_content_t *side1,
+              svn_element_content_t *side2,
+              svn_element_content_t *yca,
+              const merge_conflict_policy_t *policy,
+              apr_pool_t *result_pool,
               apr_pool_t *scratch_pool)
 {
-  const apr_array_header_t *branches;
-  SVN_ITER_T(svn_branch_state_t) *bi;
-  svn_boolean_t printed_header = FALSE;
-
-  notify_v("%s", branch_id_header_str("  ", scratch_pool));
-
-  branches = svn_branch_txn_get_branches(txn, scratch_pool);
+  svn_boolean_t same1 = svn_element_content_equal(yca, side1, scratch_pool);
+  svn_boolean_t same2 = svn_element_content_equal(yca, side2, scratch_pool);
+  svn_boolean_t conflict = FALSE;
+  svn_element_content_t *result = NULL;
 
-  for (SVN_ARRAY_ITER(bi, branches, scratch_pool))
+  if (same1)
     {
-      svn_branch_state_t *branch = bi->val;
-
-      if (svn_branch_root_eid(branch) != eid)
-        continue;
-
-      SVN_ERR(list_branch(branch, with_elements, bi->iterpool));
-      if (with_elements) /* separate branches by a blank line */
-        printf("\n");
+      result = side2;
     }
-
-  for (SVN_ARRAY_ITER(bi, branches, scratch_pool))
+  else if (same2)
     {
-      svn_branch_state_t *branch = bi->val;
-
-      if (! svn_branch_get_element(branch, eid)
-          || svn_branch_root_eid(branch) == eid)
-        continue;
-
-      if (! printed_header)
-        {
-          if (the_ui_mode == UI_MODE_PATHS)
-            notify_v("branches containing but not rooted at that element:");
-          else
-            notify_v("branches containing but not rooted at e%d:", eid);
-          printed_header = TRUE;
-        }
-      SVN_ERR(list_branch(branch, with_elements, bi->iterpool));
-      if (with_elements) /* separate branches by a blank line */
-        printf("\n");
+      result = side1;
     }
-
-  return SVN_NO_ERROR;
-}
-
-/* List all branches. If WITH_ELEMENTS is true, also list the elements
- * in each branch.
- */
-static svn_error_t *
-list_all_branches(svn_branch_txn_t *txn,
-                  svn_boolean_t with_elements,
-                  apr_pool_t *scratch_pool)
-{
-  const apr_array_header_t *branches;
-  SVN_ITER_T(svn_branch_state_t) *bi;
-
-  branches = svn_branch_txn_get_branches(txn, scratch_pool);
-
-  notify_v("branches:");
-
-  for (SVN_ARRAY_ITER(bi, branches, scratch_pool))
+  else if (yca && side1 && side2)
     {
-      svn_branch_state_t *branch = bi->val;
-
-      SVN_ERR(list_branch(branch, with_elements, bi->iterpool));
-      if (with_elements) /* separate branches by a blank line */
-        printf("\n");
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Options to control how strict the merge is about detecting conflicts.
- *
- * The options affect cases that, depending on the user's preference, could
- * either be considered a conflict or be merged to a deterministic result.
- *
- * The set of options is flexible and may be extended in future.
- */
-typedef struct merge_conflict_policy_t
-{
-  /* Whether to merge delete-vs-delete */
-  svn_boolean_t merge_double_delete;
-  /* Whether to merge add-vs-add (with same parent/name/payload) */
-  svn_boolean_t merge_double_add;
-  /* Whether to merge reparent-vs-reparent (with same parent) */
-  svn_boolean_t merge_double_reparent;
-  /* Whether to merge rename-vs-rename (with same name) */
-  svn_boolean_t merge_double_rename;
-  /* Whether to merge modify-vs-modify (with same payload) */
-  svn_boolean_t merge_double_modify;
-  /* Possible additional controls: */
-  /* merge (parent, name, props, text) independently or as a group */
-  /* merge (parent, name) independently or as a group */
-  /* merge (props, text) independently or as a group */
-} merge_conflict_policy_t;
-
-/* An element-merge conflict description.
- */
-typedef struct element_merge3_conflict_t
-{
-  svn_element_content_t *yca;
-  svn_element_content_t *side1;
-  svn_element_content_t *side2;
-} element_merge3_conflict_t;
-
-static element_merge3_conflict_t *
-element_merge3_conflict_create(svn_element_content_t *yca,
-                               svn_element_content_t *side1,
-                               svn_element_content_t *side2,
-                               apr_pool_t *result_pool)
-{
-  element_merge3_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
-
-  c->yca = yca;
-  c->side1 = side1;
-  c->side2 = side2;
-  return c;
-}
-
-/* A name-clash conflict description.
- */
-typedef struct name_clash_conflict_t
-{
-  int parent_eid;
-  const char *name;
-  /* All EIDs that conflict with each other: hash of (eid -> irrelevant). */
-  apr_hash_t *elements;
-} name_clash_conflict_t;
-
-static name_clash_conflict_t *
-name_clash_conflict_create(int parent_eid,
-                           const char *name,
-                           apr_pool_t *result_pool)
-{
-  name_clash_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
-
-  c->parent_eid = parent_eid;
-  c->name = apr_pstrdup(result_pool, name);
-  c->elements = apr_hash_make(result_pool);
-  return c;
-}
-
-/* An orphan conflict description.
- */
-typedef struct orphan_conflict_t
-{
-  svn_element_content_t *element;
-} orphan_conflict_t;
-
-static orphan_conflict_t *
-orphan_conflict_create(svn_element_content_t *element,
-                       apr_pool_t *result_pool)
-{
-  orphan_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
-
-  c->element = element;
-  return c;
-}
-
-typedef struct conflict_storage_t
-{
-  /* Single-element conflicts */
-  /* (eid -> element_merge3_conflict_t) */
-  apr_hash_t *single_element_conflicts;
-
-  /* Name-clash conflicts */
-  /* ("%{parent_eid}d/%{name}s" -> name_clash_conflict_t) */
-  apr_hash_t *name_clash_conflicts;
-
-  /* Orphan conflicts */
-  /* (eid -> orphan_conflict_t) */
-  apr_hash_t *orphan_conflicts;
-} conflict_storage_t;
-
-/*  */
-static conflict_storage_t *
-conflict_storage_create(apr_pool_t *result_pool)
-{
-  conflict_storage_t *c = apr_pcalloc(result_pool, sizeof(*c));
-
-  return c;
-}
-
-/*  */
-static const char *
-brief_eid_and_name_or_nil(svn_element_content_t *e,
-                          apr_pool_t *result_pool)
-{
-  return e ? apr_psprintf(result_pool, "%d/%s", e->parent_eid, e->name)
-           : "<nil>";
-
-  return SVN_NO_ERROR;
-}
-
-/*  */
-static svn_error_t *
-display_conflicts(conflict_storage_t *conflict_storage,
-                  const char *prefix,
-                  apr_pool_t *scratch_pool)
-{
-  apr_hash_index_t *hi;
-
-  for (hi = apr_hash_first(scratch_pool,
-                           conflict_storage->single_element_conflicts);
-       hi; hi = apr_hash_next(hi))
-    {
-      int eid = svn_int_hash_this_key(hi);
-      element_merge3_conflict_t *c = apr_hash_this_val(hi);
-
-      printf("%ssingle-element conflict: e%d: yca=%s, side1=%s, side2=%s\n",
-             prefix, eid,
-             brief_eid_and_name_or_nil(c->yca, scratch_pool),
-             brief_eid_and_name_or_nil(c->side1, scratch_pool),
-             brief_eid_and_name_or_nil(c->side2, scratch_pool));
-    }
-  for (hi = apr_hash_first(scratch_pool,
-                           conflict_storage->name_clash_conflicts);
-       hi; hi = apr_hash_next(hi))
-    {
-      /*const char *key = apr_hash_this_key(hi);*/
-      name_clash_conflict_t *c = apr_hash_this_val(hi);
-      apr_hash_index_t *hi2;
-
-      printf("%sname-clash conflict: peid %d, name '%s', %d elements\n",
-             prefix, c->parent_eid, c->name, apr_hash_count(c->elements));
-      for (hi2 = apr_hash_first(scratch_pool, c->elements);
-           hi2; hi2 = apr_hash_next(hi2))
-        {
-          int eid = svn_int_hash_this_key(hi2);
-
-          printf("%s  element %d\n", prefix, eid);
-        }
-    }
-  for (hi = apr_hash_first(scratch_pool,
-                           conflict_storage->orphan_conflicts);
-       hi; hi = apr_hash_next(hi))
-    {
-      int eid = svn_int_hash_this_key(hi);
-      orphan_conflict_t *c = apr_hash_this_val(hi);
-
-      printf("%sorphan conflict: element %d/%s: peid %d does not exist\n",
-             prefix, eid, c->element->name, c->element->parent_eid);
-    }
-  return SVN_NO_ERROR;
-}
-
-/* Merge the payload for one element.
- *
- * If there is no conflict, set *CONFLICT_P to FALSE and *RESULT_P to the
- * merged element; otherwise set *CONFLICT_P to TRUE and *RESULT_P to NULL.
- * Note that *RESULT_P can be null, indicating a deletion.
- *
- * This handles any case where at least one of (SIDE1, SIDE2, YCA) exists.
- *
- * Allocate the result in RESULT_POOL and/or as pointers to the inputs.
- */
-static void
-payload_merge(svn_element_payload_t **result_p,
-              svn_boolean_t *conflict_p,
-              int eid,
-              svn_element_payload_t *side1,
-              svn_element_payload_t *side2,
-              svn_element_payload_t *yca,
-              const merge_conflict_policy_t *policy,
-              apr_pool_t *result_pool,
-              apr_pool_t *scratch_pool)
-{
-  svn_boolean_t conflict = FALSE;
-  svn_element_payload_t *result = NULL;
-
-  if (yca && side1 && side2)
-    {
-      if (svn_element_payload_equal(side1, yca, scratch_pool))
-        {
-          result = side2;
-        }
-      else if (svn_element_payload_equal(side2, yca, scratch_pool))
-        {
-          result = side1;
-        }
-      else if (policy->merge_double_modify
-               && svn_element_payload_equal(side1, side2, scratch_pool))
-        {
-          SVN_DBG(("e%d double modify: ... -> { ... | ... }",
-                   eid));
-          result = side1;
-        }
-      else
-        {
-          /* ### Need not conflict if can merge props and text separately. */
-
-          SVN_DBG(("e%d conflict: payload: ... -> { ... | ... }",
-                   eid));
-          conflict = TRUE;
-        }
-    }
-
-  *result_p = result;
-  *conflict_p = conflict;
-}
-
-/* Merge the content for one element.
- *
- * If there is no conflict, set *CONFLICT_P to FALSE and *RESULT_P to the
- * merged element; otherwise set *CONFLICT_P to TRUE and *RESULT_P to NULL.
- * Note that *RESULT_P can be null, indicating a deletion.
- *
- * This handles any case where at least one of (SIDE1, SIDE2, YCA) exists.
- *
- * Allocate the result in RESULT_POOL and/or as pointers to the inputs.
- */
-static void
-element_merge(svn_element_content_t **result_p,
-              element_merge3_conflict_t **conflict_p,
-              int eid,
-              svn_element_content_t *side1,
-              svn_element_content_t *side2,
-              svn_element_content_t *yca,
-              const merge_conflict_policy_t *policy,
-              apr_pool_t *result_pool,
-              apr_pool_t *scratch_pool)
-{
-  svn_boolean_t same1 = svn_element_content_equal(yca, side1, scratch_pool);
-  svn_boolean_t same2 = svn_element_content_equal(yca, side2, scratch_pool);
-  svn_boolean_t conflict = FALSE;
-  svn_element_content_t *result = NULL;
-
-  if (same1)
-    {
-      result = side2;
-    }
-  else if (same2)
-    {
-      result = side1;
-    }
-  else if (yca && side1 && side2)
-    {
-      /* All three sides are different, and all exist */
-      result = apr_pmemdup(result_pool, yca, sizeof(*result));
+      /* All three sides are different, and all exist */
+      result = apr_pmemdup(result_pool, yca, sizeof(*result));
 
       /* merge the parent-eid */
       if (side1->parent_eid == yca->parent_eid)
@@ -1518,3092 +438,464 @@ element_merge(svn_element_content_t **re
       else if (strcmp(side2->name, yca->name) == 0)
         {
           result->name = side1->name;
-        }
-      else if (policy->merge_double_rename
-               && strcmp(side1->name, side2->name) == 0)
-        {
-          SVN_DBG(("e%d double rename: %s -> { %s | %s }",
-                   eid, yca->name, side1->name, side2->name));
-          result->name = side1->name;
-        }
-      else
-        {
-          SVN_DBG(("e%d conflict: name: %s -> { %s | %s }",
-                   eid, yca->name, side1->name, side2->name));
-          conflict = TRUE;
-        }
-
-      /* merge the payload */
-      {
-        svn_boolean_t payload_conflict;
-
-        payload_merge(&result->payload, &payload_conflict,
-                      eid, side1->payload, side2->payload, yca->payload,
-                      policy, result_pool, scratch_pool);
-        if (payload_conflict)
-          conflict = TRUE;
-      }
-    }
-  else if (! side1 && ! side2)
-    {
-      /* Double delete (as we assume at least one of YCA/SIDE1/SIDE2 exists) */
-      if (policy->merge_double_delete)
-        {
-          SVN_DBG(("e%d double delete",
-                   eid));
-          result = side1;
-        }
-      else
-        {
-          SVN_DBG(("e%d conflict: delete vs. delete",
-                   eid));
-          conflict = TRUE;
-        }
-    }
-  else if (side1 && side2)
-    {
-      /* Double add (as we already handled the case where YCA also exists) */
-      /* May be allowed for equal content of a normal element (not subbranch) */
-      if (policy->merge_double_add
-          && !side1->payload->is_subbranch_root
-          && !side2->payload->is_subbranch_root
-          && svn_element_content_equal(side1, side2, scratch_pool))
-        {
-          SVN_DBG(("e%d double add",
-                   eid));
-          result = side1;
-        }
-      else
-        {
-          SVN_DBG(("e%d conflict: add vs. add (%s)",
-                   eid,
-                   svn_element_content_equal(side1, side2, scratch_pool)
-                     ? "same content" : "different content"));
-          conflict = TRUE;
-        }
-    }
-  else
-    {
-      /* The remaining cases must be delete vs. modify */
-      SVN_DBG(("e%d conflict: delete vs. modify: %d -> { %d | %d }",
-               eid, !!yca, !!side1, !!side2));
-      conflict = TRUE;
-    }
-
-  *result_p = result;
-  *conflict_p
-    = conflict ? element_merge3_conflict_create(yca, side1, side2,
-                                                result_pool) : NULL;
-}
-
-static svn_error_t *
-branch_merge_subtree_r(svn_branch_txn_t *edit_txn,
-                       conflict_storage_t **conflict_storage_p,
-                       const svn_branch_el_rev_id_t *src,
-                       const svn_branch_el_rev_id_t *tgt,
-                       const svn_branch_el_rev_id_t *yca,
-                       apr_pool_t *result_pool,
-                       apr_pool_t *scratch_pool);
-
-/* Merge the subbranch of {SRC, TGT, YCA} found at EID.
- */
-static svn_error_t *
-merge_subbranch(svn_branch_txn_t *edit_txn,
-                const svn_branch_el_rev_id_t *src,
-                const svn_branch_el_rev_id_t *tgt,
-                const svn_branch_el_rev_id_t *yca,
-                int eid,
-                apr_pool_t *scratch_pool)
-{
-  svn_branch_state_t *src_subbranch
-    = svn_branch_get_subbranch_at_eid(src->branch, eid, scratch_pool);
-  svn_branch_state_t *tgt_subbranch
-    = svn_branch_get_subbranch_at_eid(tgt->branch, eid, scratch_pool);
-  svn_branch_state_t *yca_subbranch
-    = svn_branch_get_subbranch_at_eid(yca->branch, eid, scratch_pool);
-  svn_branch_el_rev_id_t *subbr_src = NULL;
-  svn_branch_el_rev_id_t *subbr_tgt = NULL;
-  svn_branch_el_rev_id_t *subbr_yca = NULL;
-
-  if (src_subbranch)
-    subbr_src = svn_branch_el_rev_id_create(
-                  src_subbranch, svn_branch_root_eid(src_subbranch),
-                  src->rev, scratch_pool);
-  if (tgt_subbranch)
-    subbr_tgt = svn_branch_el_rev_id_create(
-                  tgt_subbranch, svn_branch_root_eid(tgt_subbranch),
-                  tgt->rev, scratch_pool);
-  if (yca_subbranch)
-    subbr_yca = svn_branch_el_rev_id_create(
-                  yca_subbranch, svn_branch_root_eid(yca_subbranch),
-                  yca->rev, scratch_pool);
-
-  if (subbr_src && subbr_tgt && subbr_yca)  /* ?edit vs. ?edit */
-    {
-      conflict_storage_t *conflict_storage;
-
-      /* subbranch possibly changed in source => merge */
-      SVN_ERR(branch_merge_subtree_r(edit_txn,
-                                     &conflict_storage,
-                                     subbr_src, subbr_tgt, subbr_yca,
-                                     scratch_pool, scratch_pool));
-      /* ### store this branch's conflict_storage somewhere ... */
-    }
-  else if (subbr_src && subbr_yca)  /* ?edit vs. delete */
-    {
-      /* ### possible conflict (edit vs. delete) */
-    }
-  else if (subbr_tgt && subbr_yca)  /* delete vs. ?edit */
-    {
-      /* ### possible conflict (delete vs. edit) */
-    }
-  else if (subbr_src && subbr_tgt)  /* double add */
-    {
-      /* ### conflict */
-    }
-  else if (subbr_src)  /* added on source branch */
-    {
-      const char *new_branch_id
-        = svn_branch_id_nest(svn_branch_get_id(tgt->branch, scratch_pool),
-                             eid, scratch_pool);
-      svn_branch_rev_bid_eid_t *from
-        = svn_branch_rev_bid_eid_create(src_subbranch->txn->rev,
-                                        svn_branch_get_id(src_subbranch,
-                                                          scratch_pool),
-                                        svn_branch_root_eid(src_subbranch),
-                                        scratch_pool);
-
-      SVN_ERR(svn_branch_txn_branch(edit_txn, NULL /*new_branch_id_p*/, from,
-                                    new_branch_id, scratch_pool, scratch_pool));
-    }
-  else if (subbr_tgt)  /* added on target branch */
-    {
-      /* nothing to do */
-    }
-  else if (subbr_yca)  /* double delete */
-    {
-      /* ### conflict? policy option? */
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*  */
-static int
-sort_compare_items_by_peid_and_name(const svn_sort__item_t *a,
-                                    const svn_sort__item_t *b)
-{
-  svn_element_content_t *element_a = a->value;
-  svn_element_content_t *element_b = b->value;
-
-  if (element_a->parent_eid != element_b->parent_eid)
-    return element_a->parent_eid - element_b->parent_eid;
-  return strcmp(element_a->name, element_b->name);
-}
-
-/* Return all (key -> name_clash_conflict_t) name clash conflicts in BRANCH.
- */
-static svn_error_t *
-detect_clashes(apr_hash_t **clashes_p,
-               svn_branch_state_t *branch,
-               apr_pool_t *result_pool,
-               apr_pool_t *scratch_pool)
-{
-  apr_hash_t *clashes = apr_hash_make(result_pool);
-  SVN_ITER_T(svn_element_content_t) *pi;
-  int prev_eid = -1;
-  svn_element_content_t *prev_element = NULL;
-
-  for (SVN_HASH_ITER_SORTED(pi, svn_branch_get_elements(branch),
-                            sort_compare_items_by_peid_and_name, scratch_pool))
-    {
-      int eid = *(const int *)(pi->key);
-      svn_element_content_t *element = pi->val;
-
-      if (prev_element
-          && element->parent_eid == prev_element->parent_eid
-          && strcmp(element->name, prev_element->name) == 0)
-        {
-          const char *key = apr_psprintf(result_pool, "%d/%s",
-                                         element->parent_eid, element->name);
-          name_clash_conflict_t *c;
-
-          c = svn_hash_gets(clashes, key);
-          if (!c)
-            {
-              c = name_clash_conflict_create(
-                    element->parent_eid, element->name,
-                    result_pool);
-              svn_hash_sets(clashes, key, c);
-            }
-          svn_int_hash_set(c->elements, eid, &c);
-          svn_int_hash_set(c->elements, prev_eid, &c);
-        }
-      prev_eid = eid;
-      prev_element = element;
-    }
-
-  *clashes_p = clashes;
-  return SVN_NO_ERROR;
-}
-
-/* Return all (eid -> orphan_conflict_t) orphan conflicts in BRANCH.
- */
-static svn_error_t *
-detect_orphans(apr_hash_t **orphans_p,
-               svn_branch_state_t *branch,
-               apr_pool_t *result_pool,
-               apr_pool_t *scratch_pool)
-{
-  apr_hash_t *orphans = apr_hash_make(result_pool);
-  SVN_ITER_T(svn_element_content_t) *pi;
-  const svn_element_tree_t *elements = svn_branch_get_element_tree(branch);
-
-  for (SVN_HASH_ITER(pi, scratch_pool, elements->e_map))
-    {
-      int eid = *(const int *)(pi->key);
-      svn_element_content_t *element = pi->val;
-
-      if (eid != elements->root_eid
-          && ! svn_element_tree_get(elements, element->parent_eid))
-        {
-          orphan_conflict_t *c;
-
-          c = orphan_conflict_create(element, result_pool);
-          svn_int_hash_set(orphans, eid, c);
-
-          notify("  orphan: %d/%s: peid %d does not exist",
-                 eid, element->name, element->parent_eid);
-        }
-    }
-
-  *orphans_p = orphans;
-  return SVN_NO_ERROR;
-}
-
-/* Merge ...
- *
- * Merge any sub-branches in the same way, recursively.
- */
-static svn_error_t *
-branch_merge_subtree_r(svn_branch_txn_t *edit_txn,
-                       conflict_storage_t **conflict_storage_p,
-                       const svn_branch_el_rev_id_t *src,
-                       const svn_branch_el_rev_id_t *tgt,
-                       const svn_branch_el_rev_id_t *yca,
-                       apr_pool_t *result_pool,
-                       apr_pool_t *scratch_pool)
-{
-  svn_branch_subtree_t *s_src, *s_tgt, *s_yca;
-  apr_hash_t *diff_yca_src, *diff_yca_tgt;
-  apr_hash_t *e_conflicts = apr_hash_make(scratch_pool);
-  conflict_storage_t *conflict_storage = conflict_storage_create(result_pool);
-  SVN_ITER_T(svn_element_content_t *) *pi;
-  apr_hash_t *all_elements;
-  const merge_conflict_policy_t policy = { TRUE, TRUE, TRUE, TRUE, TRUE };
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-
-  SVN_ERR_ASSERT(src->eid == tgt->eid);
-  SVN_ERR_ASSERT(src->eid == yca->eid);
-
-  SVN_DBG(("merge src: r%2ld %s e%3d",
-           src->rev,
-           svn_branch_get_id(src->branch, scratch_pool), src->eid));
-  SVN_DBG(("merge tgt: r%2ld %s e%3d",
-           tgt->rev,
-           svn_branch_get_id(tgt->branch, scratch_pool), tgt->eid));
-  SVN_DBG(("merge yca: r%2ld %s e%3d",
-           yca->rev,
-           svn_branch_get_id(yca->branch, scratch_pool), yca->eid));
-
-  notify_v("merging into branch %s",
-           svn_branch_get_id(tgt->branch, scratch_pool));
-  /*
-      for (eid, diff1) in element_differences(YCA, FROM):
-        diff2 = element_diff(eid, YCA, TO)
-        if diff1 and diff2:
-          result := element_merge(diff1, diff2)
-        elif diff1:
-          result := diff1.right
-        # else no change
-   */
-  s_src = svn_branch_get_subtree(src->branch, src->eid, scratch_pool);
-  s_tgt = svn_branch_get_subtree(tgt->branch, tgt->eid, scratch_pool);
-  s_yca = svn_branch_get_subtree(yca->branch, yca->eid, scratch_pool);
-  SVN_ERR(element_differences(&diff_yca_src,
-                              s_yca->tree, s_src->tree,
-                              scratch_pool, scratch_pool));
-  /* ### We only need to query for YCA:TO differences in elements that are
-         different in YCA:FROM, but right now we ask for all differences. */
-  SVN_ERR(element_differences(&diff_yca_tgt,
-                              s_yca->tree, s_tgt->tree,
-                              scratch_pool, scratch_pool));
-
-  all_elements = apr_hash_overlay(scratch_pool,
-                                  svn_branch_get_elements(src->branch),
-                                  svn_branch_get_elements(tgt->branch));
-  all_elements = apr_hash_overlay(scratch_pool,
-                                  svn_branch_get_elements(yca->branch),
-                                  all_elements);
-  for (SVN_HASH_ITER_SORTED(pi, all_elements,
-                            sort_compare_items_by_eid, scratch_pool))
-    {
-      int eid = *(const int *)(pi->key);
-      svn_element_content_t **e_yca_src
-        = svn_int_hash_get(diff_yca_src, eid);
-      svn_element_content_t **e_yca_tgt
-        = svn_int_hash_get(diff_yca_tgt, eid);
-      svn_element_content_t *e_yca;
-      svn_element_content_t *e_src;
-      svn_element_content_t *e_tgt;
-      svn_element_content_t *result;
-      element_merge3_conflict_t *conflict;
-
-      svn_pool_clear(iterpool);
-
-      /* If an element hasn't changed in the source branch, there is
-         no need to do anything with it in the target branch. We could
-         use element_merge() for any case where at least one of (SRC,
-         TGT, YCA) exists, but we choose to skip it when SRC == YCA. */
-      if (! e_yca_src)
-        {
-          /* Still need to merge any subbranch linked to this element.
-             There were no changes to the link element but that doesn't
-             mean there were no changes to the linked branch. */
-          SVN_ERR(merge_subbranch(edit_txn, src, tgt, yca, eid, iterpool));
-
-          continue;
-        }
-
-      e_yca = e_yca_src[0];
-      e_src = e_yca_src[1];
-      e_tgt = e_yca_tgt ? e_yca_tgt[1] : e_yca_src[0];
-
-      element_merge(&result, &conflict,
-                    eid, e_src, e_tgt, e_yca,
-                    &policy,
-                    scratch_pool, scratch_pool);
-
-      if (conflict)
-        {
-          notify_v("!    e%d <conflict>", eid);
-          svn_int_hash_set(e_conflicts, eid, conflict);
-        }
-      else if (e_tgt && result)
-        {
-          notify_v("M/V  e%d %s%s",
-                   eid, result->name,
-                   subbranch_str(tgt->branch, eid, iterpool));
-
-          SVN_ERR(svn_branch_state_alter_one(tgt->branch, eid,
-                                             result->parent_eid, result->name,
-                                             result->payload, iterpool));
-
-          SVN_ERR(merge_subbranch(edit_txn, src, tgt, yca, eid, iterpool));
-        }
-      else if (e_tgt)
-        {
-          notify_v("D    e%d %s%s",
-                   eid, e_yca->name,
-                   subbranch_str(yca->branch, eid, iterpool));
-          SVN_ERR(svn_branch_state_delete_one(tgt->branch, eid, iterpool));
-
-          /* ### If this is a subbranch-root element being deleted, shouldn't
-             we see if there were any changes to be merged in the subbranch,
-             and raise a delete-vs-edit conflict if so? */
-        }
-      else if (result)
-        {
-          notify_v("A    e%d %s%s",
-                   eid, result->name,
-                   subbranch_str(src->branch, eid, iterpool));
-
-          /* In BRANCH, create an instance of the element EID with new content.
-           *
-           * Translated to old language, this means create a new node-copy
-           * copied (branched) from the source-right version of the merge
-           * (which is not specified here, but will need to be),
-           * which may be in this branch or in another branch.
-           */
-          SVN_ERR(svn_branch_state_alter_one(tgt->branch, eid,
-                                             result->parent_eid, result->name,
-                                             result->payload, iterpool));
-
-          SVN_ERR(merge_subbranch(edit_txn, src, tgt, yca, eid, iterpool));
-        }
-    }
-  svn_pool_destroy(iterpool);
-
-  /* Detect clashes.
-     ### TODO: Detect clashes, cycles and orphans; and report full conflict
-               info (including the relevant incoming changes) for each
-               kind of conflict. If there are no conflicts, flatten the
-               merge result into a tree. */
-  conflict_storage->single_element_conflicts = e_conflicts;
-  SVN_ERR(detect_clashes(&conflict_storage->name_clash_conflicts,
-                         tgt->branch,
-                         result_pool, scratch_pool));
-  SVN_ERR(detect_orphans(&conflict_storage->orphan_conflicts,
-                         tgt->branch,
-                         result_pool, scratch_pool));
-
-  notify_v("merging into branch %s -- finished",
-           svn_branch_get_id(tgt->branch, scratch_pool));
-
-  *conflict_storage_p = conflict_storage;
-  return SVN_NO_ERROR;
-}
-
-/* Merge SRC into TGT, using the common ancestor YCA.
- *
- * Merge the two sets of changes: YCA -> SRC and YCA -> TGT, applying
- * the result to the transaction at TGT.
- *
- * If conflicts arise, just fail.
- *
- * SRC, TGT and YCA must be existing and corresponding (same EID) elements.
- *
- * None of SRC, TGT and YCA is a subbranch root element.
- *
- * Nested subbranches will also be merged.
- */
-static svn_error_t *
-svn_branch_merge(svn_branch_txn_t *edit_txn,
-                 conflict_storage_t **conflict_storage_p,
-                 svn_branch_el_rev_id_t *src,
-                 svn_branch_el_rev_id_t *tgt,
-                 svn_branch_el_rev_id_t *yca,
-                 apr_pool_t *scratch_pool)
-{
-  /*SVN_ERR(verify_exists_in_branch(from, scratch_pool));*/
-  /*SVN_ERR(verify_exists_in_branch(to, scratch_pool));*/
-  /*SVN_ERR(verify_exists_in_branch(yca, scratch_pool));*/
-  if (src->eid != tgt->eid || src->eid != yca->eid)
-    return svn_error_createf(SVN_ERR_BRANCHING, NULL,
-                             _("Merge branches must all be same element "
-                               "(from: e%d, to: e%d, yca: e%d)"),
-                             src->eid, tgt->eid, yca->eid);
-  /*SVN_ERR(verify_not_subbranch_root(from, scratch_pool));*/
-  /*SVN_ERR(verify_not_subbranch_root(to, scratch_pool));*/
-  /*SVN_ERR(verify_not_subbranch_root(yca, scratch_pool));*/
-
-  SVN_ERR(branch_merge_subtree_r(edit_txn,
-                                 conflict_storage_p,
-                                 src, tgt, yca,
-                                 scratch_pool, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* Switch the WC to revision BASE_REVISION (SVN_INVALID_REVNUM means HEAD)
- * and branch BRANCH_ID.
- *
- * Merge any changes in the existing txn into the new txn.
- */
-static svn_error_t *
-do_switch(svnmover_wc_t *wc,
-          svn_revnum_t revision,
-          svn_branch_state_t *target_branch,
-          apr_pool_t *scratch_pool)
-{
-  const char *target_branch_id
-    = svn_branch_get_id(target_branch, scratch_pool);
-  /* Keep hold of the previous WC txn */
-  svn_branch_state_t *previous_base_br = wc->base->branch;
-  svn_branch_state_t *previous_working_br = wc->working->branch;
-  svn_boolean_t has_local_changes = TRUE /* ### wc_has_local_changes(wc) */;
-
-  if (has_local_changes
-      && svn_branch_root_eid(target_branch)
-         != svn_branch_root_eid(previous_base_br))
-    {
-      return svn_error_createf(SVN_ERR_BRANCHING, NULL,
-                               _("Cannot switch to branch '%s' and preserve "
-                                 "local changes as the new root element e%d "
-                                 "is not related to the old root element e%d"),
-                               target_branch_id,
-                               svn_branch_root_eid(target_branch),
-                               svn_branch_root_eid(previous_base_br));
-    }
-
-  /* Complete the old edit drive into the 'WC' txn */
-  SVN_ERR(svn_branch_txn_sequence_point(wc->edit_txn, scratch_pool));
-
-  /* Check out a new WC, re-using the same data object */
-  SVN_ERR(wc_checkout(wc, revision, target_branch_id, scratch_pool));
-
-  if (has_local_changes)
-    {
-      svn_branch_el_rev_id_t *yca, *src, *tgt;
-      conflict_storage_t *conflicts;
-
-      /* Merge changes from the old into the new WC */
-      yca = svn_branch_el_rev_id_create(previous_base_br,
-                                        svn_branch_root_eid(previous_base_br),
-                                        previous_base_br->txn->rev,
-                                        scratch_pool);
-      src = svn_branch_el_rev_id_create(previous_working_br,
-                                        svn_branch_root_eid(previous_working_br),
-                                        SVN_INVALID_REVNUM, scratch_pool);
-      tgt = svn_branch_el_rev_id_create(wc->working->branch,
-                                        svn_branch_root_eid(wc->working->branch),
-                                        SVN_INVALID_REVNUM, scratch_pool);
-      SVN_ERR(svn_branch_merge(wc->edit_txn, &conflicts,
-                               src, tgt, yca, scratch_pool));
-
-      if (apr_hash_count(conflicts->single_element_conflicts)
-          || apr_hash_count(conflicts->name_clash_conflicts)
-          || apr_hash_count(conflicts->orphan_conflicts))
-        {
-          SVN_ERR(display_conflicts(conflicts, "switch: ", scratch_pool));
-          return svn_error_createf(
-                   SVN_ERR_BRANCHING, NULL,
-                   _("Switch failed because of conflicts: "
-                     "%d single-element conflicts, "
-                     "%d name-clash conflicts, "
-                     "%d orphan conflicts"),
-                   apr_hash_count(conflicts->single_element_conflicts),
-                   apr_hash_count(conflicts->name_clash_conflicts),
-                   apr_hash_count(conflicts->orphan_conflicts));
-        }
-      else
-        {
-          SVN_DBG(("Switch completed: no conflicts"));
-        }
-
-      /* ### TODO: If the merge raises conflicts, either revert to the
-             pre-update state or store and handle the conflicts. Currently
-             this just leaves the merge partially done and raises an error. */
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*  */
-typedef struct diff_item_t
-{
-  int eid;
-  svn_element_content_t *e0, *e1;
-  const char *relpath0, *relpath1;
-  svn_boolean_t modified, reparented, renamed;
-} diff_item_t;
-
-/* Return differences between branch subtrees S_LEFT and S_RIGHT.
- *
- * Set *DIFF_CHANGES to a hash of (eid -> diff_item_t).
- *
- * ### This requires 'subtrees' only in order to produce the 'relpath'
- *     fields in the output. Other than that, it would work with arbitrary
- *     sets of elements.
- */
-static svn_error_t *
-subtree_diff(apr_hash_t **diff_changes,
-             svn_branch_subtree_t *s_left,
-             svn_branch_subtree_t *s_right,
-             apr_pool_t *result_pool,
-             apr_pool_t *scratch_pool)
-{
-  apr_hash_t *diff_left_right;
-  apr_hash_index_t *hi;
-
-  *diff_changes = apr_hash_make(result_pool);
-
-  SVN_ERR(element_differences(&diff_left_right,
-                              s_left->tree, s_right->tree,
-                              result_pool, scratch_pool));
-
-  for (hi = apr_hash_first(scratch_pool, diff_left_right);
-       hi; hi = apr_hash_next(hi))
-    {
-      int eid = svn_int_hash_this_key(hi);
-      svn_element_content_t **e_pair = apr_hash_this_val(hi);
-      svn_element_content_t *e0 = e_pair[0], *e1 = e_pair[1];
-
-      if (e0 || e1)
-        {
-          diff_item_t *item = apr_palloc(result_pool, sizeof(*item));
-
-          item->eid = eid;
-          item->e0 = e0;
-          item->e1 = e1;
-          item->relpath0 = e0 ? svn_element_tree_get_path_by_eid(
-                                  s_left->tree, eid, result_pool) : NULL;
-          item->relpath1 = e1 ? svn_element_tree_get_path_by_eid(
-                                  s_right->tree, eid, result_pool) : NULL;
-          item->reparented = (e0 && e1 && e0->parent_eid != e1->parent_eid);
-          item->renamed = (e0 && e1 && strcmp(e0->name, e1->name) != 0);
-
-          svn_int_hash_set(*diff_changes, eid, item);
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Find the relative order of diff items A and B, according to the
- * "major path" of each. The major path means its right-hand relpath, if
- * it exists on the right-hand side of the diff, else its left-hand relpath.
- *
- * Return negative/zero/positive when A sorts before/equal-to/after B.
- */
-static int
-diff_ordering_major_paths(const struct svn_sort__item_t *a,
-                          const struct svn_sort__item_t *b)
-{
-  const diff_item_t *item_a = a->value, *item_b = b->value;
-  int deleted_a = (item_a->e0 && ! item_a->e1);
-  int deleted_b = (item_b->e0 && ! item_b->e1);
-  const char *major_path_a = (item_a->e1 ? item_a->relpath1 : item_a->relpath0);
-  const char *major_path_b = (item_b->e1 ? item_b->relpath1 : item_b->relpath0);
-
-  /* Sort deleted items before all others */
-  if (deleted_a != deleted_b)
-    return deleted_b - deleted_a;
-
-  /* Sort by path */
-  return svn_path_compare_paths(major_path_a, major_path_b);
-}
-
-/* Display differences between subtrees LEFT and RIGHT, which are subtrees
- * of branches LEFT_BID and RIGHT_BID respectively.
- *
- * Use EDITOR to fetch content when needed.
- *
- * Write a line containing HEADER before any other output, if it is not
- * null. Write PREFIX at the start of each line of output, including any
- * header line. PREFIX and HEADER should contain no end-of-line characters.
- *
- * The output refers to paths or to elements according to THE_UI_MODE.
- */
-static svn_error_t *
-show_subtree_diff(svn_branch_subtree_t *left,
-                  const char *left_bid,
-                  svn_branch_subtree_t *right,
-                  const char *right_bid,
-                  const char *prefix,
-                  const char *header,
-                  apr_pool_t *scratch_pool)
-{
-  apr_hash_t *diff_changes;
-  SVN_ITER_T(diff_item_t) *ai;
-
-  SVN_ERR_ASSERT(left && left->tree->root_eid >= 0
-                 && right && right->tree->root_eid >= 0);
-
-  SVN_ERR(subtree_diff(&diff_changes, left, right,
-                       scratch_pool, scratch_pool));
-
-  if (header && apr_hash_count(diff_changes))
-    notify("%s%s", prefix, header);
-
-  for (SVN_HASH_ITER_SORTED(ai, diff_changes,
-                            (the_ui_mode == UI_MODE_EIDS)
-                              ? sort_compare_items_by_eid
-                              : diff_ordering_major_paths,
-                            scratch_pool))
-    {
-      diff_item_t *item = ai->val;
-      svn_element_content_t *e0 = item->e0, *e1 = item->e1;
-      char status_mod = (e0 && e1) ? 'M' : e0 ? 'D' : 'A';
-
-      /* For a deleted element whose parent was also deleted, mark it is
-         less interesting, somehow. (Or we could omit it entirely.) */
-      if (status_mod == 'D')
-        {
-          diff_item_t *parent_item
-            = svn_int_hash_get(diff_changes, e0->parent_eid);
-
-          if (parent_item && ! parent_item->e1)
-            status_mod = 'd';
-        }
-
-      if (the_ui_mode == UI_MODE_PATHS)
-        {
-          const char *major_path = (e1 ? item->relpath1 : item->relpath0);
-          const char *from = "";
-
-          if (item->reparented || item->renamed)
-            {
-              if (! item->reparented)
-                from = apr_psprintf(scratch_pool,
-                                    " (renamed from .../%s)",
-                                    e0->name);
-              else if (! item->renamed)
-                from = apr_psprintf(scratch_pool,
-                                    " (moved from %s/...)",
-                                    svn_relpath_dirname(item->relpath0,
-                                                        scratch_pool));
-              else
-                from = apr_psprintf(scratch_pool,
-                                    " (moved+renamed from %s)",
-                                    item->relpath0);
-            }
-          notify("%s%c%c%c %s%s%s",
-                 prefix,
-                 status_mod,
-                 item->reparented ? 'v' : ' ', item->renamed ? 'r' : ' ',
-                 major_path,
-                 subtree_subbranch_str(e0 ? left : right,
-                                       e0 ? left_bid : right_bid,
-                                       item->eid, scratch_pool),
-                 from);
-        }
-      else
-        {
-          notify("%s%c%c%c e%-3d  %s%s%s%s%s",
-                 prefix,
-                 status_mod,
-                 item->reparented ? 'v' : ' ', item->renamed ? 'r' : ' ',
-                 item->eid,
-                 e1 ? peid_name(e1, scratch_pool) : "",
-                 subtree_subbranch_str(e0 ? left : right,
-                                       e0 ? left_bid : right_bid,
-                                       item->eid, scratch_pool),
-                 e0 && e1 ? " (from " : "",
-                 e0 ? peid_name(e0, scratch_pool) : "",
-                 e0 && e1 ? ")" : "");
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-typedef svn_error_t *
-svn_branch_diff_func_t(svn_branch_subtree_t *left,
-                       const char *left_bid,
-                       svn_branch_subtree_t *right,
-                       const char *right_bid,
-                       const char *prefix,
-                       const char *header,
-                       apr_pool_t *scratch_pool);
-
-/* Display differences between subtrees LEFT and RIGHT.
- *
- * Recurse into sub-branches.
- */
-static svn_error_t *
-subtree_diff_r(svn_branch_subtree_t *left,
-               svn_revnum_t left_rev,
-               const char *left_bid,
-               const char *left_rrpath,
-               svn_branch_subtree_t *right,
-               svn_revnum_t right_rev,
-               const char *right_bid,
-               const char *right_rrpath,
-               svn_branch_diff_func_t diff_func,
-               const char *prefix,
-               apr_pool_t *scratch_pool)
-{
-  const char *left_str
-    = left ? apr_psprintf(scratch_pool, "r%ld:%s:e%d at /%s",
-                          left_rev, left_bid, left->tree->root_eid, left_rrpath)
-           : NULL;
-  const char *right_str
-    = right ? apr_psprintf(scratch_pool, "r%ld:%s:e%d at /%s",
-                           right_rev, right_bid, right->tree->root_eid, right_rrpath)
-            : NULL;
-  const char *header;
-  apr_hash_t *subbranches_l, *subbranches_r, *subbranches_all;
-  apr_hash_index_t *hi;
-
-  SVN_DBG(("subtree_diff_r: l='%s' r='%s'",
-           left ? left_rrpath : "<nil>",
-           right ? right_rrpath : "<nil>"));
-
-  if (!left)
-    {
-      header = apr_psprintf(scratch_pool,
-                 "--- added branch %s",
-                 right_str);
-      notify("%s%s", prefix, header);
-    }
-  else if (!right)
-    {
-      header = apr_psprintf(scratch_pool,
-                 "--- deleted branch %s",
-                 left_str);
-      notify("%s%s", prefix, header);
-    }
-  else
-    {
-      if (strcmp(left_str, right_str) == 0)
-        {
-          header = apr_psprintf(
-                     scratch_pool, "--- diff branch %s",
-                     left_str);
-        }
-      else
-        {
-          header = apr_psprintf(
-                     scratch_pool, "--- diff branch %s : %s",
-                     left_str, right_str);
-        }
-      SVN_ERR(diff_func(left, left_bid, right, right_bid,
-                        prefix, header,
-                        scratch_pool));
-    }
-
-  /* recurse into each subbranch that exists in LEFT and/or in RIGHT */
-  subbranches_l = left ? left->subbranches : apr_hash_make(scratch_pool);
-  subbranches_r = right ? right->subbranches : apr_hash_make(scratch_pool);
-  subbranches_all = apr_hash_overlay(scratch_pool,
-                                     subbranches_l, subbranches_r);
-
-  for (hi = apr_hash_first(scratch_pool, subbranches_all);
-       hi; hi = apr_hash_next(hi))
-    {
-      int e = svn_int_hash_this_key(hi);
-      svn_branch_subtree_t *sub_left = NULL, *sub_right = NULL;
-      const char *sub_left_bid = NULL, *sub_right_bid = NULL;
-      const char *sub_left_rrpath = NULL, *sub_right_rrpath = NULL;
-
-      /* recurse */
-      if (left)
-        {
-          sub_left = svn_branch_subtree_get_subbranch_at_eid(left, e,
-                                                             scratch_pool);
-          if (sub_left)
-            {
-              const char *relpath
-                = svn_element_tree_get_path_by_eid(left->tree, e, scratch_pool);
-
-              sub_left_bid = svn_branch_id_nest(left_bid, e, scratch_pool);
-              sub_left_rrpath = svn_relpath_join(left_rrpath, relpath,
-                                                 scratch_pool);
-            }
-        }
-      if (right)
-        {
-          sub_right = svn_branch_subtree_get_subbranch_at_eid(right, e,
-                                                              scratch_pool);
-          if (sub_right)
-            {

[... 2596 lines stripped ...]