Modified: subversion/branches/ra-git/subversion/svn/conflict-callbacks.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/svn/conflict-callbacks.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/svn/conflict-callbacks.c (original)
+++ subversion/branches/ra-git/subversion/svn/conflict-callbacks.c Tue Oct 11 
09:11:50 2016
@@ -40,58 +40,13 @@
 #include "cl-conflicts.h"
 
 #include "private/svn_cmdline_private.h"
-#include "private/svn_wc_private.h"
 
 #include "svn_private_config.h"
 
 #define ARRAY_LEN(ary) ((sizeof (ary)) / (sizeof ((ary)[0])))
-#define MAX_ARRAY_LEN(aryx, aryz)               \
-  (ARRAY_LEN((aryx)) > ARRAY_LEN((aryz))        \
-   ? ARRAY_LEN((aryx)) : ARRAY_LEN((aryz)))
 
 
 
-struct svn_cl__interactive_conflict_baton_t {
-  svn_cl__accept_t accept_which;
-  apr_hash_t *config;
-  const char *editor_cmd;
-  svn_boolean_t external_failed;
-  svn_cmdline_prompt_baton_t *pb;
-  const char *path_prefix;
-  svn_boolean_t quit;
-  svn_cl__conflict_stats_t *conflict_stats;
-  svn_boolean_t printed_summary;
-};
-
-svn_error_t *
-svn_cl__get_conflict_func_interactive_baton(
-  svn_cl__interactive_conflict_baton_t **b,
-  svn_cl__accept_t accept_which,
-  apr_hash_t *config,
-  const char *editor_cmd,
-  svn_cl__conflict_stats_t *conflict_stats,
-  svn_cancel_func_t cancel_func,
-  void *cancel_baton,
-  apr_pool_t *result_pool)
-{
-  svn_cmdline_prompt_baton_t *pb = apr_palloc(result_pool, sizeof(*pb));
-  pb->cancel_func = cancel_func;
-  pb->cancel_baton = cancel_baton;
-
-  *b = apr_palloc(result_pool, sizeof(**b));
-  (*b)->accept_which = accept_which;
-  (*b)->config = config;
-  (*b)->editor_cmd = editor_cmd;
-  (*b)->external_failed = FALSE;
-  (*b)->pb = pb;
-  SVN_ERR(svn_dirent_get_absolute(&(*b)->path_prefix, "", result_pool));
-  (*b)->quit = FALSE;
-  (*b)->conflict_stats = conflict_stats;
-  (*b)->printed_summary = FALSE;
-
-  return SVN_NO_ERROR;
-}
-
 svn_cl__accept_t
 svn_cl__accept_from_word(const char *word)
 {
@@ -131,7 +86,7 @@ svn_cl__accept_from_word(const char *wor
 /* Print on stdout a diff that shows incoming conflicting changes
  * corresponding to the conflict described in CONFLICT. */
 static svn_error_t *
-show_diff(const svn_client_conflict_t *conflict,
+show_diff(svn_client_conflict_t *conflict,
           const char *merged_abspath,
           const char *path_prefix,
           svn_cancel_func_t cancel_func,
@@ -213,7 +168,7 @@ show_diff(const svn_client_conflict_t *c
 /* Print on stdout just the conflict hunks of a diff among the 'base', 'their'
  * and 'my' files of CONFLICT. */
 static svn_error_t *
-show_conflicts(const svn_client_conflict_t *conflict,
+show_conflicts(svn_client_conflict_t *conflict,
                svn_cancel_func_t cancel_func,
                void *cancel_baton,
                apr_pool_t *pool)
@@ -383,23 +338,19 @@ edit_prop_conflict(const svn_string_t **
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool)
 {
-  apr_file_t *file;
   const char *file_path;
   svn_boolean_t performed_edit = FALSE;
   svn_stream_t *merged_prop;
 
-  SVN_ERR(svn_io_open_unique_file3(&file, &file_path, NULL,
-                                   svn_io_file_del_on_pool_cleanup,
-                                   result_pool, scratch_pool));
-  merged_prop = svn_stream_from_aprfile2(file, TRUE /* disown */,
-                                         scratch_pool);
+  SVN_ERR(svn_stream_open_unique(&merged_prop, &file_path, NULL,
+                                 svn_io_file_del_on_pool_cleanup,
+                                 scratch_pool, scratch_pool));
   SVN_ERR(merge_prop_conflict(merged_prop, base_propval, my_propval,
                               their_propval, NULL,
                               pb->cancel_func,
                               pb->cancel_baton,
                               scratch_pool));
   SVN_ERR(svn_stream_close(merged_prop));
-  SVN_ERR(svn_io_file_flush(file, scratch_pool));
   SVN_ERR(open_editor(&performed_edit, file_path, editor_cmd,
                       config, scratch_pool));
   if (performed_edit && merged_propval)
@@ -416,159 +367,161 @@ edit_prop_conflict(const svn_string_t **
 /* Maximum line length for the prompt string. */
 #define MAX_PROMPT_WIDTH 70
 
-/* Description of a resolver option */
+/* Description of a resolver option.
+ * Resolver options are used to build the resolver's conflict prompt.
+ * The user types a code to select the corresponding conflict resolution 
option.
+ * Some resolver options have a corresponding --accept argument. */
 typedef struct resolver_option_t
 {
   const char *code;        /* one or two characters */
   const char *short_desc;  /* label in prompt (localized) */
   const char *long_desc;   /* longer description (localized) */
   svn_client_conflict_option_id_t choice;
-                           /* or ..._undefined if not a simple choice */
+                           /* or ..._undefined if not from libsvn_client */
+  const char *accept_arg;  /* --accept option argument (NOT localized) */
 } resolver_option_t;
 
-/* Resolver options for a text conflict */
-/* (opt->code == "" causes a blank line break in help_string()) */
-static const resolver_option_t text_conflict_options[] =
+/* Resolver options for conflict options offered by libsvn_client.  */
+static const resolver_option_t builtin_resolver_options[] =
+{
+  { "r",  NULL, NULL,
+                                  svn_client_conflict_option_merged_text,
+                                  SVN_CL__ACCEPT_WORKING },
+  { "mc", NULL, NULL,
+    svn_client_conflict_option_working_text_where_conflicted,
+                                  SVN_CL__ACCEPT_MINE_CONFLICT },
+  { "tc", NULL, NULL,
+    svn_client_conflict_option_incoming_text_where_conflicted,
+                                  SVN_CL__ACCEPT_THEIRS_CONFLICT },
+  { "mf", NULL, NULL,
+                                  svn_client_conflict_option_working_text,
+                                  SVN_CL__ACCEPT_MINE_FULL},
+  { "tf", NULL, NULL,
+                                  svn_client_conflict_option_incoming_text,
+                                  SVN_CL__ACCEPT_THEIRS_FULL },
+  { "p",  N_("postpone"),         NULL,
+                                  svn_client_conflict_option_postpone,
+                                  SVN_CL__ACCEPT_POSTPONE },
+
+  /* This option resolves a tree conflict to the current working copy state. */
+  { "r", NULL, NULL,
+    svn_client_conflict_option_accept_current_wc_state,
+    SVN_CL__ACCEPT_WORKING },
+
+  /* These options use the same code since they only occur in
+   * distinct conflict scenarios. */
+  { "u", N_("update move destination"),    NULL,
+    svn_client_conflict_option_update_move_destination },
+  { "u", N_("update any moved-away children"), NULL,
+    svn_client_conflict_option_update_any_moved_away_children },
+
+  /* Options for incoming add vs local add. */
+  { "i", N_("ignore incoming addition"), NULL,
+    svn_client_conflict_option_incoming_add_ignore },
+
+  /* Options for incoming file add vs local file add upon merge. */
+  { "m", N_("merge the files"), NULL,
+    svn_client_conflict_option_incoming_added_file_text_merge },
+  { "R", N_("delete my file and replace it with incoming file"), NULL,
+    svn_client_conflict_option_incoming_added_file_replace },
+  { "M", N_("replace my file with incoming file and merge the files"), NULL,
+    svn_client_conflict_option_incoming_added_file_replace_and_merge },
+
+  /* Options for incoming dir add vs local dir add upon merge. */
+  { "m", N_("merge the directories"), NULL,
+    svn_client_conflict_option_incoming_added_dir_merge },
+  { "R", N_("delete my directory and replace it with incoming directory"), 
NULL,
+    svn_client_conflict_option_incoming_added_dir_replace },
+  { "M", N_("replace my directory with incoming directory and merge"), NULL,
+    svn_client_conflict_option_incoming_added_dir_replace_and_merge },
+
+  /* Options for incoming delete vs any. */
+  { "i", N_("ignore incoming deletion"), NULL,
+    svn_client_conflict_option_incoming_delete_ignore },
+  { "a", N_("accept incoming deletion"), NULL,
+    svn_client_conflict_option_incoming_delete_accept },
+
+  /* Options for incoming move vs local edit. */
+  { "m", NULL, NULL, svn_client_conflict_option_incoming_move_file_text_merge 
},
+  { "m", NULL, NULL, svn_client_conflict_option_incoming_move_dir_merge },
+
+  { NULL }
+};
+
+/* Extra resolver options offered by 'svn' for any conflict. */
+static const resolver_option_t extra_resolver_options[] =
 {
   /* Translators: keep long_desc below 70 characters (wrap with a left
-     margin of 9 spaces if needed); don't translate the words within square
-     brackets. */
-  { "e",  N_("edit file"),        N_("change merged file in an editor"
-                                     "  [edit]"),
-                                  svn_client_conflict_option_undefined },
+     margin of 9 spaces if needed) */
+  { "q",  N_("quit resolution"),  N_("postpone all remaining conflicts"),
+                                  svn_client_conflict_option_postpone },
+  { NULL }
+};
+
+
+/* Additional resolver options offered by 'svn' for a text conflict. */
+static const resolver_option_t extra_resolver_options_text[] =
+{
+  /* Translators: keep long_desc below 70 characters (wrap with a left
+     margin of 9 spaces if needed) */
+  { "e",  N_("edit file"),        N_("change merged file in an editor"),
+                                  svn_client_conflict_option_undefined,
+                                  SVN_CL__ACCEPT_EDIT },
   { "df", N_("show diff"),        N_("show all changes made to merged file"),
-                                  svn_client_conflict_option_undefined },
-  { "r",  N_("mark resolved"),   N_("accept merged version of file  
[working]"),
-                                  svn_client_conflict_option_merged_text },
-  { "",   "",                     "", svn_client_conflict_option_unspecified },
+                                  svn_client_conflict_option_undefined},
   { "dc", N_("display conflict"), N_("show all conflicts "
                                      "(ignoring merged version)"),
                                   svn_client_conflict_option_undefined },
-  { "mc", N_("my side of conflict"), N_("accept my version for all conflicts "
-                                        "(same)  [mine-conflict]"),
-                                  
svn_client_conflict_option_working_text_where_conflicted },
-  { "tc", N_("their side of conflict"), N_("accept their version for all "
-                                           "conflicts (same)"
-                                           "  [theirs-conflict]"),
-                                  
svn_client_conflict_option_incoming_text_where_conflicted },
-  { "",   "",                     "", svn_client_conflict_option_unspecified },
-  { "mf", N_("my version"),       N_("accept my version of entire file (even "
-                                     "non-conflicts)  [mine-full]"),
-                                  svn_client_conflict_option_working_text },
-  { "tf", N_("their version"),    N_("accept their version of entire file "
-                                     "(same)  [theirs-full]"),
-                                  svn_client_conflict_option_incoming_text },
-  { "",   "",                     "", svn_client_conflict_option_unspecified },
   { "m",  N_("merge"),            N_("use merge tool to resolve conflict"),
                                   svn_client_conflict_option_undefined },
   { "l",  N_("launch tool"),      N_("launch external merge tool to resolve "
-                                     "conflict  [launch]"),
-                                  svn_client_conflict_option_undefined },
+                                     "conflict"),
+                                  svn_client_conflict_option_undefined,
+                                  SVN_CL__ACCEPT_LAUNCH },
   { "i",  N_("internal merge tool"), N_("use built-in merge tool to "
                                      "resolve conflict"),
                                   svn_client_conflict_option_undefined },
-  { "p",  N_("postpone"),         N_("mark the conflict to be resolved later"
-                                     "  [postpone]"),
-                                  svn_client_conflict_option_postpone },
-  { "q",  N_("quit resolution"),  N_("postpone all remaining conflicts"),
-                                  svn_client_conflict_option_postpone },
   { "s",  N_("show all options"), N_("show this list (also 'h', '?')"),
                                   svn_client_conflict_option_undefined },
   { NULL }
 };
 
-/* Resolver options for a binary file conflict. */
-static const resolver_option_t binary_conflict_options[] =
+/* Additional resolver options offered by 'svn' for a property conflict. */
+static const resolver_option_t extra_resolver_options_prop[] =
 {
   /* Translators: keep long_desc below 70 characters (wrap with a left
-     margin of 9 spaces if needed); don't translate the words within square
-     brackets. */
-  { "r",  N_("mark resolved"),   N_("accept the working copy version of file "
-                                    " [working]"),
-                                  svn_client_conflict_option_merged_text },
-  { "tf", N_("their version"),    N_("accept the incoming version of file "
-                                     " [theirs-full]"),
-                                  svn_client_conflict_option_incoming_text },
-  { "p",  N_("postpone"),         N_("mark the conflict to be resolved later "
-                                     " [postpone]"),
-                                  svn_client_conflict_option_postpone },
-  { "q",  N_("quit resolution"),  N_("postpone all remaining conflicts"),
-                                  svn_client_conflict_option_postpone },
-  { "s",  N_("show all options"), N_("show this list (also 'h', '?')"),
-                                  svn_client_conflict_option_undefined },
-  { NULL }
-};
-
-/* Resolver options for a property conflict */
-static const resolver_option_t prop_conflict_options[] =
-{
-  { "mf", N_("my version"),       N_("accept my version of entire property 
(even "
-                                     "non-conflicts)  [mine-full]"),
-                                  svn_client_conflict_option_working_text },
-  { "tf", N_("their version"),    N_("accept their version of entire property "
-                                     "(same)  [theirs-full]"),
-                                  svn_client_conflict_option_incoming_text },
+     margin of 9 spaces if needed) */
   { "dc", N_("display conflict"), N_("show conflicts in this property"),
                                   svn_client_conflict_option_undefined },
-  { "e",  N_("edit property"),    N_("change merged property value in an 
editor"
-                                     "  [edit]"),
-                                  svn_client_conflict_option_undefined },
-  { "r",  N_("mark resolved"),    N_("accept edited version of property"),
-                                  svn_client_conflict_option_merged_text },
-  { "p",  N_("postpone"),         N_("mark the conflict to be resolved later"
-                                     "  [postpone]"),
-                                  svn_client_conflict_option_postpone },
-  { "q",  N_("quit resolution"),  N_("postpone all remaining conflicts"),
-                                  svn_client_conflict_option_postpone },
+  { "e",  N_("edit property"),    N_("change merged property value in an "
+                                     "editor"),
+                                  svn_client_conflict_option_undefined,
+                                  SVN_CL__ACCEPT_EDIT },
   { "h",  N_("help"),             N_("show this help (also '?')"),
                                   svn_client_conflict_option_undefined },
   { NULL }
 };
 
-/* Resolver options for a tree conflict */
-static const resolver_option_t tree_conflict_options[] =
+/* Additional resolver options offered by 'svn' for a tree conflict. */
+static const resolver_option_t extra_resolver_options_tree[] =
 {
-  { "r",  N_("mark resolved"),    N_("accept current working copy state"),
-                                  svn_client_conflict_option_merged_text },
-  { "p",  N_("postpone"),         N_("resolve the conflict later  [postpone]"),
-                                  svn_client_conflict_option_postpone },
-  { "q",  N_("quit resolution"),  N_("postpone all remaining conflicts"),
-                                  svn_client_conflict_option_postpone },
-  { "h",  N_("help"),             N_("show this help (also '?')"),
+  /* Translators: keep long_desc below 70 characters (wrap with a left
+     margin of 9 spaces if needed) */
+  { "d",  N_("set repository move destination path"),
+          N_("pick repository move target from list of possible targets"),
                                   svn_client_conflict_option_undefined },
-  { NULL }
-};
 
-static const resolver_option_t tree_conflict_options_update_moved_away[] =
-{
-  { "mc", N_("apply update to move destination (recommended)"),
-                                  N_("apply incoming update to move 
destination"
-                                     "  [mine-conflict]"),
-                                  
svn_client_conflict_option_working_text_where_conflicted },
-  { "p",  N_("postpone"),         N_("resolve the conflict later  [postpone]"),
-                                  svn_client_conflict_option_postpone },
-  { "q",  N_("quit resolution"),  N_("postpone all remaining conflicts"),
-                                  svn_client_conflict_option_postpone },
-  { "h",  N_("help"),             N_("show this help (also '?')"),
+  { "w",  N_("set working copy move destination path"),
+          N_("pick working copy move target from list of possible targets"),
                                   svn_client_conflict_option_undefined },
-  { NULL }
-};
 
-static const resolver_option_t tree_conflict_options_update_edit_deleted_dir[] 
=
-{
-  { "mc", N_("prepare for updating moved-away children, if any (recommended)"),
-                                  N_("allow updating moved-away children "
-                                     "with 'svn resolve' [mine-conflict]"),
-                                  
svn_client_conflict_option_working_text_where_conflicted },
-  { "p",  N_("postpone"),         N_("resolve the conflict later  [postpone]"),
-                                  svn_client_conflict_option_postpone },
-  { "q",  N_("quit resolution"),  N_("postpone all remaining conflicts"),
-                                  svn_client_conflict_option_postpone },
   { "h",  N_("help"),             N_("show this help (also '?')"),
                                   svn_client_conflict_option_undefined },
+
   { NULL }
 };
 
+
 /* Return a pointer to the option description in OPTIONS matching the
  * one- or two-character OPTION_CODE.  Return NULL if not found. */
 static const resolver_option_t *
@@ -586,6 +539,22 @@ find_option(const resolver_option_t *opt
   return NULL;
 }
 
+/* Return a pointer to the option description in OPTIONS matching the
+ * the conflict option ID CHOICE.  Return NULL if not found. */
+static const resolver_option_t *
+find_option_by_id(const resolver_option_t *options,
+                  svn_client_conflict_option_id_t choice)
+{
+  const resolver_option_t *opt;
+
+  for (opt = options; opt->code; opt++)
+    {
+      if (opt->choice == choice)
+        return opt;
+    }
+  return NULL;
+}
+
 /* Return a prompt string listing the options OPTIONS. If OPTION_CODES is
  * non-null, select only the options whose codes are mentioned in it. */
 static const char *
@@ -610,6 +579,8 @@ prompt_string(const resolver_option_t *o
           if (! *option_codes)
             break;
           opt = find_option(options, *option_codes++);
+          if (opt == NULL)
+            continue;
         }
       else
         {
@@ -620,8 +591,8 @@ prompt_string(const resolver_option_t *o
 
       if (! first)
         result = apr_pstrcat(pool, result, ",", SVN_VA_NULL);
-      s = apr_psprintf(pool, _(" (%s) %s"),
-                       opt->code, _(opt->short_desc));
+      s = apr_psprintf(pool, " (%s) %s", opt->code,
+                       opt->short_desc ? _(opt->short_desc) : opt->long_desc);
       slen = svn_utf_cstring_utf8_width(s);
       /* Break the line if adding the next option would make it too long */
       if (this_line_len + slen > MAX_PROMPT_WIDTH)
@@ -637,33 +608,44 @@ prompt_string(const resolver_option_t *o
 }
 
 /* Return a help string listing the OPTIONS. */
-static const char *
-help_string(const resolver_option_t *options,
+static svn_error_t *
+help_string(const char **result,
+            const resolver_option_t *options,
             apr_pool_t *pool)
 {
-  const char *result = "";
   const resolver_option_t *opt;
+  apr_pool_t *iterpool;
 
+  *result = "";
+  iterpool = svn_pool_create(pool);
   for (opt = options; opt->code; opt++)
     {
+      svn_pool_clear(iterpool);
+
       /* Append a line describing OPT, or a blank line if its code is "". */
       if (opt->code[0])
         {
           const char *s = apr_psprintf(pool, "  (%s)", opt->code);
 
-          result = apr_psprintf(pool, "%s%-6s - %s\n",
-                                result, s, _(opt->long_desc));
+          if (opt->accept_arg)
+            *result = apr_psprintf(pool, "%s%-6s - %s  [%s]\n",
+                                   *result, s, opt->long_desc,
+                                   opt->accept_arg);
+          else
+            *result = apr_psprintf(pool, "%s%-6s - %s\n", *result, s,
+                                   opt->long_desc);
         }
       else
         {
-          result = apr_pstrcat(pool, result, "\n", SVN_VA_NULL);
+          *result = apr_pstrcat(pool, *result, "\n", SVN_VA_NULL);
         }
     }
-  result = apr_pstrcat(pool, result,
+  svn_pool_destroy(iterpool);
+  *result = apr_pstrcat(pool, *result,
                        _("Words in square brackets are the corresponding "
                          "--accept option arguments.\n"),
                        SVN_VA_NULL);
-  return result;
+  return SVN_NO_ERROR;
 }
 
 /* Prompt the user with CONFLICT_OPTIONS, restricted to the options listed
@@ -672,12 +654,14 @@ help_string(const resolver_option_t *opt
  * NULL if the answer was not one of them.
  *
  * If the answer is the (globally recognized) 'help' option, then display
- * the help (on stderr) and return with *OPT == NULL.
+ * CONFLICT_DESCRIPTION (if not NULL) and help (on stderr) and return with
+ * *OPT == NULL.
  */
 static svn_error_t *
 prompt_user(const resolver_option_t **opt,
             const resolver_option_t *conflict_options,
             const char *const *options_to_show,
+            const char *conflict_description,
             void *prompt_baton,
             apr_pool_t *scratch_pool)
 {
@@ -688,9 +672,13 @@ prompt_user(const resolver_option_t **op
   SVN_ERR(svn_cmdline_prompt_user2(&answer, prompt, prompt_baton, 
scratch_pool));
   if (strcmp(answer, "h") == 0 || strcmp(answer, "?") == 0)
     {
-      SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n",
-                                  help_string(conflict_options,
-                                              scratch_pool)));
+      const char *helpstr;
+
+      if (conflict_description)
+        SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n",
+                                    conflict_description));
+      SVN_ERR(help_string(&helpstr, conflict_options, scratch_pool));
+      SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n", helpstr));
       *opt = NULL;
     }
   else
@@ -705,27 +693,142 @@ prompt_user(const resolver_option_t **op
   return SVN_NO_ERROR;
 }
 
-/* Ask the user what to do about the text conflict described by CONFLICT.
- * Return the answer in RESULT. B is the conflict baton for this
- * conflict resolution session.
+/* Set *OPTIONS to an array of resolution options for CONFLICT. */
+static svn_error_t *
+build_text_conflict_options(resolver_option_t **options,
+                            svn_client_conflict_t *conflict,
+                            svn_client_ctx_t *ctx,
+                            svn_boolean_t is_binary,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool)
+{
+  resolver_option_t *opt;
+  const resolver_option_t *o;
+  apr_array_header_t *builtin_options;
+  apr_size_t nopt;
+  int i;
+  apr_pool_t *iterpool;
+
+  SVN_ERR(svn_client_conflict_text_get_resolution_options(&builtin_options,
+                                                          conflict, ctx,
+                                                          scratch_pool,
+                                                          scratch_pool));
+  nopt = builtin_options->nelts + ARRAY_LEN(extra_resolver_options);
+  if (!is_binary)
+    nopt += ARRAY_LEN(extra_resolver_options_text);
+  *options = apr_pcalloc(result_pool, sizeof(*opt) * (nopt + 1));
+
+  opt = *options;
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < builtin_options->nelts; i++)
+    {
+      svn_client_conflict_option_t *builtin_option;
+      svn_client_conflict_option_id_t id;
+      const resolver_option_t *known_option;
+
+      svn_pool_clear(iterpool);
+      builtin_option = APR_ARRAY_IDX(builtin_options, i,
+                                     svn_client_conflict_option_t *);
+      id = svn_client_conflict_option_get_id(builtin_option);
+      known_option = find_option_by_id(builtin_resolver_options, id);
+      if (known_option == NULL)
+        continue; /* ### unknown option -- assign a code dynamically? */
+
+      opt->code = known_option->code;
+      opt->short_desc = known_option->short_desc;
+      SVN_ERR(svn_client_conflict_option_describe(&opt->long_desc,
+                                                  builtin_option,
+                                                  result_pool,
+                                                  iterpool));
+      opt->choice = id;
+      opt->accept_arg = known_option->accept_arg;
+      opt++; 
+    }
+
+  for (o = extra_resolver_options; o->code; o++)
+    *opt++ = *o;
+  if (!is_binary)
+    {
+      for (o = extra_resolver_options_text; o->code; o++)
+        *opt++ = *o;
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+/* Mark CONFLICT as resolved to resolution option with ID OPTION_ID.
+ * If TEXT_CONFLICTED is true, resolve text conflicts described by CONFLICT.
+ * IF PROPNAME is not NULL, mark the conflict in the specified property as
+ * resolved. If PROPNAME is "", mark all property conflicts described by
+ * CONFLICT as resolved.
+ * If TREE_CONFLICTED is true, resolve tree conflicts described by CONFLICT.
+ * Adjust CONFLICT_STATS as necessary (PATH_PREFIX is needed for this step). */
+static svn_error_t *
+mark_conflict_resolved(svn_client_conflict_t *conflict,
+                       svn_client_conflict_option_id_t option_id,
+                       svn_boolean_t text_conflicted,
+                       const char *propname,
+                       svn_boolean_t tree_conflicted,
+                       const char *path_prefix,
+                       svn_cl__conflict_stats_t *conflict_stats,
+                       svn_client_ctx_t *ctx,
+                       apr_pool_t *scratch_pool)
+{
+  const char *local_relpath
+    = svn_cl__local_style_skip_ancestor(
+        path_prefix, svn_client_conflict_get_local_abspath(conflict),
+        scratch_pool);
+
+  if (text_conflicted)
+    {
+      SVN_ERR(svn_client_conflict_text_resolve_by_id(conflict, option_id,
+                                                     ctx, scratch_pool));
+      svn_cl__conflict_stats_resolved(conflict_stats, local_relpath,
+                                      svn_wc_conflict_kind_text);
+    }
+
+  if (propname)
+    {
+      SVN_ERR(svn_client_conflict_prop_resolve_by_id(conflict, propname,
+                                                     option_id, ctx,
+                                                     scratch_pool));
+      svn_cl__conflict_stats_resolved(conflict_stats, local_relpath,
+                                      svn_wc_conflict_kind_property);
+    }
+
+  if (tree_conflicted)
+    {
+      SVN_ERR(svn_client_conflict_tree_resolve_by_id(conflict, option_id,
+                                                     ctx, scratch_pool));
+      svn_cl__conflict_stats_resolved(conflict_stats, local_relpath,
+                                      svn_wc_conflict_kind_tree);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Ask the user what to do about the text conflict described by CONFLICT
+ * and either resolve the conflict accordingly or postpone resolution.
  * SCRATCH_POOL is used for temporary allocations. */
 static svn_error_t *
-handle_text_conflict(svn_client_conflict_option_id_t *option_id,
-                     svn_boolean_t *save_merged,
-                     svn_cl__accept_t *accept_which,
+handle_text_conflict(svn_boolean_t *resolved,
+                     svn_boolean_t *postponed,
                      svn_boolean_t *quit,
-                     const svn_client_conflict_t *conflict,
+                     svn_boolean_t *printed_description,
+                     svn_client_conflict_t *conflict,
                      const char *path_prefix,
                      svn_cmdline_prompt_baton_t *pb,
                      const char *editor_cmd,
                      apr_hash_t *config,
+                     svn_cl__conflict_stats_t *conflict_stats,
+                     svn_client_ctx_t *ctx,
                      apr_pool_t *scratch_pool)
 {
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   svn_boolean_t diff_allowed = FALSE;
-  /* Have they done something that might have affected the merged
-     file (so that we need to save a .edited copy by setting the
-     *save_merge flag)? */
+  /* Have they done something that might have affected the merged file? */
   svn_boolean_t performed_edit = FALSE;
   /* Have they done *something* (edit, look at diff, etc) to
      give them a rational basis for choosing (r)esolved? */
@@ -739,6 +842,10 @@ handle_text_conflict(svn_client_conflict
   const char *my_abspath;
   const char *their_abspath;
   const char *merged_abspath = svn_client_conflict_get_local_abspath(conflict);
+  resolver_option_t *text_conflict_options;
+  svn_client_conflict_option_id_t option_id; 
+
+  option_id = svn_client_conflict_option_unspecified;
 
   SVN_ERR(svn_client_conflict_text_get_contents(NULL, &my_abspath,
                                                 &base_abspath, &their_abspath,
@@ -749,14 +856,19 @@ handle_text_conflict(svn_client_conflict
                                                     local_abspath,
                                                     scratch_pool);
 
-  if (is_binary)
-    SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
-                                _("Conflict discovered in binary file 
'%s'.\n"),
-                                local_relpath));
-  else
-    SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
-                                _("Conflict discovered in file '%s'.\n"),
-                                local_relpath));
+  if (!*printed_description)
+    {
+      if (is_binary)
+        SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
+                                    _("Merge conflict discovered in binary "
+                                      "file '%s'.\n"),
+                                    local_relpath));
+      else
+        SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
+                                    _("Merge conflict discovered in file 
'%s'.\n"),
+                                    local_relpath));
+      *printed_description = TRUE;
+    }
 
   /* ### TODO This whole feature availability check is grossly outdated.
      DIFF_ALLOWED needs either to be redefined or to go away.
@@ -771,17 +883,15 @@ handle_text_conflict(svn_client_conflict
       || (!base_abspath && my_abspath && their_abspath)))
     diff_allowed = TRUE;
 
+  SVN_ERR(build_text_conflict_options(&text_conflict_options, conflict, ctx,
+                                      is_binary, scratch_pool, scratch_pool));
   while (TRUE)
     {
-      const char *options[1 + MAX_ARRAY_LEN(binary_conflict_options,
-                                            text_conflict_options)];
-
-      const resolver_option_t *conflict_options = is_binary
-                                                    ? binary_conflict_options
-                                                    : text_conflict_options;
-      const char **next_option = options;
+      const char *suggested_options[9]; /* filled statically below */
+      const char **next_option = suggested_options;
       const resolver_option_t *opt;
 
+
       svn_pool_clear(iterpool);
 
       *next_option++ = "p";
@@ -799,9 +909,6 @@ handle_text_conflict(svn_client_conflict
 
           if (knows_something)
             *next_option++ = "r";
-
-          *next_option++ = "mc";
-          *next_option++ = "tc";
         }
       else
         {
@@ -819,22 +926,24 @@ handle_text_conflict(svn_client_conflict
       *next_option++ = "s";
       *next_option++ = NULL;
 
-      SVN_ERR(prompt_user(&opt, conflict_options, options, pb, iterpool));
+      SVN_ERR(prompt_user(&opt, text_conflict_options, suggested_options,
+                          NULL, pb, iterpool));
       if (! opt)
         continue;
 
       if (strcmp(opt->code, "q") == 0)
         {
-          *option_id = opt->choice;
-          *accept_which = svn_cl__accept_postpone;
+          option_id = opt->choice;
           *quit = TRUE;
           break;
         }
       else if (strcmp(opt->code, "s") == 0)
         {
+          const char *helpstr;
+
+          SVN_ERR(help_string(&helpstr, text_conflict_options, iterpool));
           SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n",
-                                      help_string(conflict_options,
-                                                  iterpool)));
+                                      helpstr));
         }
       else if (strcmp(opt->code, "dc") == 0)
         {
@@ -1032,66 +1141,146 @@ handle_text_conflict(svn_client_conflict
               continue;
             }
 
-          *option_id = opt->choice;
-          if (performed_edit && save_merged)
-            *save_merged = TRUE;
+          option_id = opt->choice;
           break;
         }
     }
   svn_pool_destroy(iterpool);
 
+  if (option_id != svn_client_conflict_option_unspecified &&
+      option_id != svn_client_conflict_option_postpone)
+    {
+      SVN_ERR(mark_conflict_resolved(conflict, option_id,
+                                     TRUE, NULL, FALSE,
+                                     path_prefix, conflict_stats,
+                                     ctx, scratch_pool));
+      *resolved = TRUE;
+    }
+  else
+    {
+      *resolved = FALSE;
+      *postponed = (option_id == svn_client_conflict_option_postpone);
+    }
+
   return SVN_NO_ERROR;
 }
 
-/* Ask the user what to do about the property conflict described by CONFLICT.
- * Return the answer in RESULT. B is the conflict baton for this
- * conflict resolution session.
+/* Set *OPTIONS to an array of resolution options for CONFLICT. */
+static svn_error_t *
+build_prop_conflict_options(resolver_option_t **options,
+                            svn_client_conflict_t *conflict,
+                            svn_client_ctx_t *ctx,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool)
+{
+  resolver_option_t *opt;
+  const resolver_option_t *o;
+  apr_array_header_t *builtin_options;
+  apr_size_t nopt;
+  int i;
+  apr_pool_t *iterpool;
+
+  SVN_ERR(svn_client_conflict_prop_get_resolution_options(&builtin_options,
+                                                          conflict, ctx,
+                                                          scratch_pool,
+                                                          scratch_pool));
+  nopt = builtin_options->nelts + ARRAY_LEN(extra_resolver_options) +
+           ARRAY_LEN(extra_resolver_options_prop);
+  *options = apr_pcalloc(result_pool, sizeof(*opt) * (nopt + 1));
+
+  opt = *options;
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < builtin_options->nelts; i++)
+    {
+      svn_client_conflict_option_t *builtin_option;
+      svn_client_conflict_option_id_t id;
+      const resolver_option_t *known_option;
+
+      svn_pool_clear(iterpool);
+      builtin_option = APR_ARRAY_IDX(builtin_options, i,
+                                     svn_client_conflict_option_t *);
+      id = svn_client_conflict_option_get_id(builtin_option);
+      known_option = find_option_by_id(builtin_resolver_options, id);
+      if (known_option == NULL)
+        continue; /* ### unknown option -- assign a code dynamically? */
+
+      opt->code = known_option->code;
+      opt->short_desc = known_option->short_desc;
+      SVN_ERR(svn_client_conflict_option_describe(&opt->long_desc,
+                                                  builtin_option,
+                                                  result_pool,
+                                                  iterpool));
+      opt->choice = id;
+      opt->accept_arg = known_option->accept_arg;
+
+      opt++; 
+    }
+
+  svn_pool_destroy(iterpool);
+
+  for (o = extra_resolver_options; o->code; o++)
+    *opt++ = *o;
+  for (o = extra_resolver_options_prop; o->code; o++)
+    *opt++ = *o;
+
+  return SVN_NO_ERROR;
+}
+
+/* Ask the user what to do about the conflicted property PROPNAME described
+ * by CONFLICT and return the corresponding resolution option in *OPTION.
  * SCRATCH_POOL is used for temporary allocations. */
 static svn_error_t *
-handle_prop_conflict(svn_client_conflict_option_id_t *option_id,
-                     const svn_string_t **merged_value,
-                     svn_cl__accept_t *accept_which,
-                     svn_boolean_t *quit,
-                     const char *path_prefix,
-                     svn_cmdline_prompt_baton_t *pb,
-                     const char *editor_cmd,
-                     apr_hash_t *config,
-                     const svn_client_conflict_t *conflict,
-                     apr_pool_t *result_pool,
-                     apr_pool_t *scratch_pool)
+handle_one_prop_conflict(svn_client_conflict_option_t **option,
+                         svn_boolean_t *quit,
+                         const char *path_prefix,
+                         svn_cmdline_prompt_baton_t *pb,
+                         const char *editor_cmd,
+                         apr_hash_t *config,
+                         svn_client_conflict_t *conflict,
+                         const char *propname,
+                         svn_client_ctx_t *ctx,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
 {
   apr_pool_t *iterpool;
-  const char *message;
+  const char *description;
   const svn_string_t *merged_propval = NULL;
   svn_boolean_t resolved_allowed = FALSE;
   const svn_string_t *base_propval;
   const svn_string_t *my_propval;
   const svn_string_t *their_propval;
+  apr_array_header_t *resolution_options;
+  resolver_option_t *prop_conflict_options;
 
   SVN_ERR(svn_client_conflict_prop_get_propvals(NULL, &my_propval,
                                                 &base_propval, &their_propval,
-                                                conflict, scratch_pool));
+                                                conflict, propname,
+                                                scratch_pool));
 
   SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
                               _("Conflict for property '%s' discovered"
                                 " on '%s'.\n"),
-                              svn_client_conflict_prop_get_propname(conflict),
+                              propname,
                               svn_cl__local_style_skip_ancestor(
                                 path_prefix,
                                 
svn_client_conflict_get_local_abspath(conflict),
                                 scratch_pool)));
-
-  SVN_ERR(svn_cl__get_human_readable_prop_conflict_description(&message,
-                                                               conflict,
-                                                               scratch_pool));
-  SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", message));
-
+  SVN_ERR(svn_client_conflict_prop_get_description(&description, conflict,
+                                                   scratch_pool, 
scratch_pool));
+  SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", description));
+
+  SVN_ERR(svn_client_conflict_prop_get_resolution_options(&resolution_options,
+                                                          conflict, ctx,
+                                                          result_pool,
+                                                          scratch_pool));
+  SVN_ERR(build_prop_conflict_options(&prop_conflict_options, conflict, ctx,
+                                      scratch_pool, scratch_pool));
   iterpool = svn_pool_create(scratch_pool);
   while (TRUE)
     {
       const resolver_option_t *opt;
-      const char *options[ARRAY_LEN(prop_conflict_options)];
-      const char **next_option = options;
+      const char *suggested_options[9]; /* filled statically below */
+      const char **next_option = suggested_options;
 
       *next_option++ = "p";
       *next_option++ = "mf";
@@ -1106,15 +1295,15 @@ handle_prop_conflict(svn_client_conflict
 
       svn_pool_clear(iterpool);
 
-      SVN_ERR(prompt_user(&opt, prop_conflict_options, options, pb,
-                          iterpool));
+      SVN_ERR(prompt_user(&opt, prop_conflict_options, suggested_options,
+                          NULL, pb, iterpool));
       if (! opt)
         continue;
 
       if (strcmp(opt->code, "q") == 0)
         {
-          *option_id = opt->choice;
-          *accept_which = svn_cl__accept_postpone;
+          *option = svn_client_conflict_option_find_by_id(resolution_options,
+                                                          opt->choice);
           *quit = TRUE;
           break;
         }
@@ -1143,13 +1332,17 @@ handle_prop_conflict(svn_client_conflict
               continue;
             }
 
-          *merged_value = merged_propval;
-          *option_id = svn_client_conflict_option_merged_text;
+          *option = svn_client_conflict_option_find_by_id(
+                      resolution_options,
+                      svn_client_conflict_option_merged_text);
+          svn_client_conflict_option_set_merged_propval(*option,
+                                                        merged_propval);
           break;
         }
       else if (opt->choice != svn_client_conflict_option_undefined)
         {
-          *option_id = opt->choice;
+          *option = svn_client_conflict_option_find_by_id(resolution_options,
+                                                          opt->choice);
           break;
         }
     }
@@ -1158,155 +1351,490 @@ handle_prop_conflict(svn_client_conflict
   return SVN_NO_ERROR;
 }
 
-/* Ask the user what to do about the tree conflict described by CONFLICT.
- * Return the answer in RESULT. B is the conflict baton for this
- * conflict resolution session.
+/* Ask the user what to do about the property conflicts described by CONFLICT
+ * and either resolve them accordingly or postpone resolution.
  * SCRATCH_POOL is used for temporary allocations. */
 static svn_error_t *
-handle_tree_conflict(svn_client_conflict_option_id_t *option_id,
-                     svn_cl__accept_t *accept_which,
+handle_prop_conflicts(svn_boolean_t *resolved,
+                      svn_boolean_t *postponed,
+                      svn_boolean_t *quit,
+                      const svn_string_t **merged_value,
+                      const char *path_prefix,
+                      svn_cmdline_prompt_baton_t *pb,
+                      const char *editor_cmd,
+                      apr_hash_t *config,
+                      svn_client_conflict_t *conflict,
+                      svn_cl__conflict_stats_t *conflict_stats,
+                      svn_client_ctx_t *ctx,
+                      apr_pool_t *result_pool,
+                      apr_pool_t *scratch_pool)
+{
+  apr_array_header_t *props_conflicted;
+  apr_pool_t *iterpool;
+  int i;
+  int nresolved = 0;
+
+  SVN_ERR(svn_client_conflict_get_conflicted(NULL, &props_conflicted, NULL,
+                                             conflict, scratch_pool,
+                                             scratch_pool));
+
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < props_conflicted->nelts; i++)
+    {
+      const char *propname = APR_ARRAY_IDX(props_conflicted, i, const char *);
+      svn_client_conflict_option_t *option;
+      svn_client_conflict_option_id_t option_id;
+
+      svn_pool_clear(iterpool);
+
+      SVN_ERR(handle_one_prop_conflict(&option, quit, path_prefix, pb,
+                                       editor_cmd, config, conflict, propname,
+                                       ctx,
+                                       iterpool, iterpool));
+      option_id = svn_client_conflict_option_get_id(option);
+
+      if (option_id != svn_client_conflict_option_unspecified &&
+          option_id != svn_client_conflict_option_postpone)
+        {
+          const char *local_relpath =
+            svn_cl__local_style_skip_ancestor(
+              path_prefix, svn_client_conflict_get_local_abspath(conflict),
+              iterpool);
+
+          SVN_ERR(svn_client_conflict_prop_resolve(conflict, propname, option,
+                                                   ctx, iterpool));
+          svn_cl__conflict_stats_resolved(conflict_stats, local_relpath,
+                                          svn_wc_conflict_kind_property);
+          nresolved++;
+          *postponed = FALSE;
+        }
+      else
+        *postponed = (option_id == svn_client_conflict_option_postpone);
+
+      if (*quit)
+        break;
+    }
+  svn_pool_destroy(iterpool);
+
+  /* Indicate success if no property conflicts remain. */
+  *resolved = (nresolved == props_conflicted->nelts);
+
+  return SVN_NO_ERROR;
+}
+
+/* Set *OPTIONS to an array of resolution options for CONFLICT. */
+static svn_error_t *
+build_tree_conflict_options(
+  resolver_option_t **options,
+  apr_array_header_t **possible_moved_to_repos_relpaths,
+  apr_array_header_t **possible_moved_to_abspaths,
+  svn_client_conflict_t *conflict,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *result_pool,
+  apr_pool_t *scratch_pool)
+{
+  resolver_option_t *opt;
+  const resolver_option_t *o;
+  apr_array_header_t *builtin_options;
+  apr_size_t nopt;
+  int i;
+  apr_pool_t *iterpool;
+
+  SVN_ERR(svn_client_conflict_tree_get_resolution_options(&builtin_options,
+                                                          conflict, ctx,
+                                                          scratch_pool,
+                                                          scratch_pool));
+  nopt = builtin_options->nelts + ARRAY_LEN(extra_resolver_options_tree) +
+           ARRAY_LEN(extra_resolver_options);
+  *options = apr_pcalloc(result_pool, sizeof(*opt) * (nopt + 1));
+  *possible_moved_to_abspaths = NULL;
+  *possible_moved_to_repos_relpaths = NULL;
+
+  opt = *options;
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < builtin_options->nelts; i++)
+    {
+      svn_client_conflict_option_t *builtin_option;
+      svn_client_conflict_option_id_t id;
+      const resolver_option_t *known_option;
+
+      svn_pool_clear(iterpool);
+      builtin_option = APR_ARRAY_IDX(builtin_options, i,
+                                     svn_client_conflict_option_t *);
+      id = svn_client_conflict_option_get_id(builtin_option);
+      known_option = find_option_by_id(builtin_resolver_options, id);
+      if (known_option == NULL)
+        continue; /* ### unknown option -- assign a code dynamically? */
+
+      opt->code = known_option->code;
+      opt->short_desc = known_option->short_desc;
+      SVN_ERR(svn_client_conflict_option_describe(&opt->long_desc,
+                                                  builtin_option,
+                                                  result_pool,
+                                                  iterpool));
+      opt->choice = id;
+      opt->accept_arg = known_option->accept_arg;
+
+      opt++; 
+
+      if (id == svn_client_conflict_option_incoming_move_file_text_merge ||
+          id == svn_client_conflict_option_incoming_move_dir_merge)
+        {
+          SVN_ERR(
+            svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+              possible_moved_to_repos_relpaths, builtin_option,
+              result_pool, iterpool));
+          SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+                    possible_moved_to_abspaths, builtin_option,
+                    result_pool, iterpool));
+        }
+    }
+
+  svn_pool_destroy(iterpool);
+
+  for (o = extra_resolver_options_tree; o->code; o++)
+    {
+      /* Add move target choice options only if there are multiple
+       * move targets to choose from. */
+      if (strcmp(o->code, "d") == 0 &&
+          (*possible_moved_to_repos_relpaths == NULL || 
+           (*possible_moved_to_repos_relpaths)->nelts <= 1))
+        continue;
+      if (strcmp(o->code, "w") == 0 &&
+          (*possible_moved_to_abspaths == NULL || 
+           (*possible_moved_to_abspaths)->nelts <= 1))
+        continue;
+
+      *opt++ = *o;
+    }
+  for (o = extra_resolver_options; o->code; o++)
+    *opt++ = *o;
+
+  return SVN_NO_ERROR;
+}
+
+/* Make the user select a move target path for the moved-away VICTIM_ABSPATH. 
*/
+static svn_error_t *
+prompt_move_target_path(int *preferred_move_target_idx,
+                        apr_array_header_t *possible_moved_to_paths,
+                        svn_boolean_t paths_are_local,
+                        svn_cmdline_prompt_baton_t *pb,
+                        const char *victim_abspath,
+                        svn_client_ctx_t *ctx,
+                        apr_pool_t *scratch_pool)
+{
+  const char *move_targets_prompt = "";
+  const char *move_targets_list = "";
+  const char *wcroot_abspath;
+  const char *victim_relpath;
+  int i;
+  apr_int64_t idx;
+  apr_pool_t *iterpool;
+
+  SVN_ERR(svn_client_get_wc_root(&wcroot_abspath, victim_abspath,
+                                 ctx, scratch_pool, scratch_pool));
+  victim_relpath = svn_cl__local_style_skip_ancestor(wcroot_abspath,
+                                                     victim_abspath,
+                                                     scratch_pool),
+  iterpool = svn_pool_create(scratch_pool);
+
+  /* Build the prompt. */
+  for (i = 0; i < possible_moved_to_paths->nelts; i++)
+    {
+      svn_pool_clear(iterpool);
+
+      if (paths_are_local)
+        {
+          const char *moved_to_abspath;
+          const char *moved_to_relpath;
+
+          moved_to_abspath = APR_ARRAY_IDX(possible_moved_to_paths, i,
+                                           const char *);
+          moved_to_relpath = svn_cl__local_style_skip_ancestor(
+                               wcroot_abspath, moved_to_abspath, iterpool),
+          move_targets_list = apr_psprintf(scratch_pool, "%s (%d): '%s'\n",
+                                           move_targets_list, i + 1,
+                                           moved_to_relpath);
+        }
+      else
+        {
+          const char *moved_to_repos_relpath;
+
+          moved_to_repos_relpath = APR_ARRAY_IDX(possible_moved_to_paths, i,
+                                                 const char *);
+          move_targets_list = apr_psprintf(scratch_pool, "%s (%d): '^/%s'\n",
+                                           move_targets_list, i + 1,
+                                           moved_to_repos_relpath);
+        }
+    }
+  if (paths_are_local)
+    move_targets_prompt =
+      apr_psprintf(scratch_pool,
+                   _("Possible working copy destinations for moved-away '%s' "
+                     "are:\n%s"
+                     "Only one destination can be a move; the others are "
+                     "copies.\n"
+                     "Specify the correct move target path by number: "),
+                   victim_relpath, move_targets_list);
+  else
+    move_targets_prompt =
+      apr_psprintf(scratch_pool,
+                   _("Possible repository destinations for moved-away '%s' "
+                     "are:\n%s"
+                     "Only one destination can be a move; the others are "
+                     "copies.\n"
+                     "Specify the correct move target path by number: "),
+                   victim_relpath, move_targets_list);
+
+  /* Keep asking the user until we got a valid choice. */
+  while (1)
+    {
+      const char *answer;
+      svn_error_t *err;
+
+      svn_pool_clear(iterpool);
+
+      SVN_ERR(svn_cmdline_prompt_user2(&answer, move_targets_prompt,
+                                       pb, iterpool));
+      err = svn_cstring_strtoi64(&idx, answer, 1,
+                                 possible_moved_to_paths->nelts, 10);
+      if (err)
+        {
+          char buf[1024];
+
+          svn_cmdline_fprintf(stderr, iterpool, "%s\n",
+                              svn_err_best_message(err, buf, sizeof(buf)));
+          svn_error_clear(err);
+          continue;
+        }
+
+      break;
+    }
+
+  svn_pool_destroy(iterpool);
+
+  *preferred_move_target_idx = (idx - 1);
+  return SVN_NO_ERROR;
+}
+
+/* Ask the user what to do about the tree conflict described by CONFLICT
+ * and either resolve the conflict accordingly or postpone resolution.
+ * SCRATCH_POOL is used for temporary allocations. */
+static svn_error_t *
+handle_tree_conflict(svn_boolean_t *resolved,
+                     svn_boolean_t *postponed,
                      svn_boolean_t *quit,
-                     const svn_client_conflict_t *conflict,
+                     svn_boolean_t *printed_description,
+                     svn_client_conflict_t *conflict,
                      const char *path_prefix,
                      svn_cmdline_prompt_baton_t *pb,
+                     svn_cl__conflict_stats_t *conflict_stats,
+                     svn_client_ctx_t *ctx,
                      apr_pool_t *scratch_pool)
 {
-  const char *readable_desc;
-  const char *src_left_version;
-  const char *src_right_version;
-  const char *repos_root_url;
-  const char *repos_relpath;
-  svn_revnum_t peg_rev;
-  svn_node_kind_t node_kind;
   apr_pool_t *iterpool;
-  
-  SVN_ERR(svn_cl__get_human_readable_tree_conflict_description(
-           &readable_desc, conflict, scratch_pool));
-  SVN_ERR(svn_cmdline_fprintf(
-               stderr, scratch_pool,
-               _("Tree conflict on '%s'\n   > %s\n"),
-               svn_cl__local_style_skip_ancestor(path_prefix,
-                 svn_client_conflict_get_local_abspath(conflict), 
scratch_pool),
-               readable_desc));
-
-  SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL, conflict,
-                                             scratch_pool, scratch_pool));
-  SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(&repos_relpath,
-                                                              &peg_rev,
-                                                              &node_kind,
-                                                              conflict,
-                                                              scratch_pool,
-                                                              scratch_pool));
-  src_left_version =
-              svn_cl__node_description(repos_root_url, repos_relpath, peg_rev,
-                                       node_kind, repos_root_url, 
scratch_pool);
-  if (src_left_version)
-    SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s: %s\n",
-                                _("Source  left"), src_left_version));
-
-  SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(&repos_relpath,
-                                                              &peg_rev,
-                                                              &node_kind,
-                                                              conflict,
-                                                              scratch_pool,
-                                                              scratch_pool));
-  src_right_version =
-              svn_cl__node_description(repos_root_url, repos_relpath, peg_rev,
-                                       node_kind, repos_root_url, 
scratch_pool);
-  if (src_right_version)
-    SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s: %s\n",
-                                _("Source right"), src_right_version));
-
+  resolver_option_t *tree_conflict_options;
+  svn_client_conflict_option_id_t option_id;
+  const char *local_abspath;
+  const char *conflict_description;
+  const char *local_change_description;
+  const char *incoming_change_description;
+  apr_array_header_t *possible_moved_to_repos_relpaths;
+  apr_array_header_t *possible_moved_to_abspaths;
+
+  option_id = svn_client_conflict_option_unspecified;
+  local_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+  /* Always show the best possible conflict description and options. */
+  SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, scratch_pool));
+
+  SVN_ERR(svn_client_conflict_tree_get_description(
+           &incoming_change_description, &local_change_description,
+           conflict, ctx, scratch_pool, scratch_pool));
+  conflict_description = apr_psprintf(scratch_pool, "%s\n%s",
+                                      incoming_change_description,
+                                      local_change_description);
+  if (!*printed_description)
+    SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
+                                _("Tree conflict on '%s':\n%s\n"),
+                                svn_cl__local_style_skip_ancestor(
+                                  path_prefix, local_abspath, scratch_pool),
+                                conflict_description));
+
+  SVN_ERR(build_tree_conflict_options(&tree_conflict_options,
+                                      &possible_moved_to_repos_relpaths,
+                                      &possible_moved_to_abspaths,
+                                      conflict, ctx,
+                                      scratch_pool, scratch_pool));
   iterpool = svn_pool_create(scratch_pool);
   while (1)
     {
       const resolver_option_t *opt;
-      const resolver_option_t *tc_opts;
 
       svn_pool_clear(iterpool);
 
-      tc_opts = tree_conflict_options;
+      SVN_ERR(prompt_user(&opt, tree_conflict_options, NULL,
+                          conflict_description, pb, iterpool));
+      *printed_description = TRUE;
+      if (! opt)
+        continue;
 
-      if (svn_client_conflict_get_operation(conflict) ==
-          svn_wc_operation_update ||
-          svn_client_conflict_get_operation(conflict) ==
-          svn_wc_operation_switch)
+      if (strcmp(opt->code, "q") == 0)
         {
-          svn_wc_conflict_reason_t reason;
-
-          reason = svn_client_conflict_get_local_change(conflict);
-          if (reason == svn_wc_conflict_reason_moved_away)
+          option_id = opt->choice;
+          *quit = TRUE;
+          break;
+        }
+      else if (strcmp(opt->code, "d") == 0)
+        {
+          int preferred_move_target_idx;
+          apr_array_header_t *options;
+          svn_client_conflict_option_t *conflict_option;
+
+          SVN_ERR(prompt_move_target_path(&preferred_move_target_idx,
+                                          possible_moved_to_repos_relpaths,
+                                          FALSE,
+                                          pb, local_abspath, ctx, iterpool));
+
+          /* Update preferred move target path. */
+          SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options,
+                                                                  conflict,
+                                                                  ctx,
+                                                                  iterpool,
+                                                                  iterpool));
+          conflict_option =
+            svn_client_conflict_option_find_by_id( 
+              options,
+              svn_client_conflict_option_incoming_move_file_text_merge);
+          if (conflict_option == NULL)
             {
-              tc_opts = tree_conflict_options_update_moved_away;
+              conflict_option =
+                svn_client_conflict_option_find_by_id( 
+                  options, svn_client_conflict_option_incoming_move_dir_merge);
             }
-          else if (reason == svn_wc_conflict_reason_deleted ||
-                   reason == svn_wc_conflict_reason_replaced)
+
+          if (conflict_option)
             {
-              if (svn_client_conflict_get_incoming_change(conflict) ==
-                  svn_wc_conflict_action_edit &&
-                  svn_client_conflict_tree_get_victim_node_kind(conflict) ==
-                  svn_node_dir)
-                tc_opts = tree_conflict_options_update_edit_deleted_dir;
+              SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(
+                        conflict_option, preferred_move_target_idx, iterpool));
+
+              /* Update option description. */
+              SVN_ERR(build_tree_conflict_options(
+                        &tree_conflict_options,
+                        &possible_moved_to_repos_relpaths,
+                        &possible_moved_to_abspaths,
+                        conflict, ctx,
+                        scratch_pool, scratch_pool));
             }
+          continue;
         }
+      else if (strcmp(opt->code, "w") == 0)
+        {
+          int preferred_move_target_idx;
+          apr_array_header_t *options;
+          svn_client_conflict_option_t *conflict_option;
+
+          SVN_ERR(prompt_move_target_path(&preferred_move_target_idx,
+                                           possible_moved_to_abspaths, TRUE,
+                                           pb, local_abspath, ctx, iterpool));
+
+          /* Update preferred move target path. */
+          SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options,
+                                                                  conflict,
+                                                                  ctx,
+                                                                  iterpool,
+                                                                  iterpool));
+          conflict_option =
+            svn_client_conflict_option_find_by_id( 
+              options,
+              svn_client_conflict_option_incoming_move_file_text_merge);
+          if (conflict_option == NULL)
+            {
+              conflict_option =
+                svn_client_conflict_option_find_by_id( 
+                  options, svn_client_conflict_option_incoming_move_dir_merge);
+            }
 
-      SVN_ERR(prompt_user(&opt, tc_opts, NULL, pb, iterpool));
-      if (! opt)
-        continue;
+          if (conflict_option)
+            {
+              SVN_ERR(svn_client_conflict_option_set_moved_to_abspath(
+                        conflict_option, preferred_move_target_idx, ctx, 
iterpool));
 
-      if (strcmp(opt->code, "q") == 0)
-        {
-          *option_id = opt->choice;
-          *accept_which = svn_cl__accept_postpone;
-          *quit = TRUE;
-          break;
+              /* Update option description. */
+              SVN_ERR(build_tree_conflict_options(
+                        &tree_conflict_options,
+                        &possible_moved_to_repos_relpaths,
+                        &possible_moved_to_abspaths,
+                        conflict, ctx,
+                        scratch_pool, scratch_pool));
+            }
+          continue;
         }
       else if (opt->choice != svn_client_conflict_option_undefined)
         {
-          *option_id = opt->choice;
+          option_id = opt->choice;
           break;
         }
     }
   svn_pool_destroy(iterpool);
+  if (option_id != svn_client_conflict_option_unspecified &&
+      option_id != svn_client_conflict_option_postpone)
+    {
+      SVN_ERR(mark_conflict_resolved(conflict, option_id,
+                                     FALSE, NULL, TRUE,
+                                     path_prefix, conflict_stats,
+                                     ctx, scratch_pool));
+      *resolved = TRUE;
+    }
+  else
+    {
+      *resolved = FALSE;
+      *postponed = (option_id == svn_client_conflict_option_postpone);
+    }
 
   return SVN_NO_ERROR;
 }
 
-/* The body of svn_cl__conflict_func_interactive(). */
 static svn_error_t *
-conflict_func_interactive(svn_client_conflict_option_id_t *option_id,
-                          svn_boolean_t *save_merged,
-                          const svn_string_t **merged_propval,
-                          svn_cl__accept_t *accept_which, 
-                          svn_boolean_t *quit,
-                          svn_boolean_t *external_failed,
-                          svn_boolean_t *printed_summary,
-                          const svn_client_conflict_t *conflict,
-                          const char *editor_cmd,
-                          apr_hash_t *config,
-                          const char *path_prefix,
-                          svn_cmdline_prompt_baton_t *pb,
-                          svn_cl__conflict_stats_t *conflict_stats,
-                          apr_pool_t *result_pool,
-                          apr_pool_t *scratch_pool)
+resolve_conflict_by_accept_option(svn_client_conflict_option_id_t *option_id,
+                                  svn_cl__accept_t accept_which, 
+                                  svn_boolean_t *external_failed,
+                                  svn_client_conflict_t *conflict,
+                                  const char *editor_cmd,
+                                  apr_hash_t *config,
+                                  const char *path_prefix,
+                                  svn_cmdline_prompt_baton_t *pb,
+                                  svn_cl__conflict_stats_t *conflict_stats,
+                                  svn_client_ctx_t *ctx,
+                                  apr_pool_t *result_pool,
+                                  apr_pool_t *scratch_pool)
 {
   svn_error_t *err;
   const char *base_abspath = NULL;
   const char *my_abspath = NULL;
   const char *their_abspath = NULL;
   const char *merged_abspath = svn_client_conflict_get_local_abspath(conflict);
+  svn_boolean_t text_conflicted;
+  apr_array_header_t *props_conflicted;
+  svn_boolean_t tree_conflicted;
+
+  SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+                                             &props_conflicted,
+                                             &tree_conflicted,
+                                             conflict,
+                                             scratch_pool,
+                                             scratch_pool));
 
-  if (svn_client_conflict_get_kind(conflict) == svn_wc_conflict_kind_text)
+  if (text_conflicted)
     SVN_ERR(svn_client_conflict_text_get_contents(NULL, &my_abspath,
                                                   &base_abspath,
                                                   &their_abspath,
                                                   conflict, scratch_pool,
                                                   scratch_pool));
 
-  /* Start out assuming we're going to postpone the conflict. */
-  *option_id = svn_client_conflict_option_postpone;
-
-  switch (*accept_which)
+  *option_id = svn_client_conflict_option_unspecified;
+  /* Handle the --accept option. */ 
+  switch (accept_which)
     {
     case svn_cl__accept_invalid:
     case svn_cl__accept_unspecified:
@@ -1314,32 +1842,32 @@ conflict_func_interactive(svn_client_con
       break;
     case svn_cl__accept_postpone:
       *option_id = svn_client_conflict_option_postpone;
-      return SVN_NO_ERROR;
+      break;
     case svn_cl__accept_base:
       *option_id = svn_client_conflict_option_base_text;
-      return SVN_NO_ERROR;
+      break;
     case svn_cl__accept_working:
       *option_id = svn_client_conflict_option_merged_text;
-      return SVN_NO_ERROR;
+      break;
     case svn_cl__accept_mine_conflict:
       *option_id = svn_client_conflict_option_working_text_where_conflicted;
-      return SVN_NO_ERROR;
+      break;
     case svn_cl__accept_theirs_conflict:
       *option_id = svn_client_conflict_option_incoming_text_where_conflicted;
-      return SVN_NO_ERROR;
+      break;
     case svn_cl__accept_mine_full:
       *option_id = svn_client_conflict_option_working_text;
-      return SVN_NO_ERROR;
+      break;
     case svn_cl__accept_theirs_full:
       *option_id = svn_client_conflict_option_incoming_text;
-      return SVN_NO_ERROR;
+      break;
     case svn_cl__accept_edit:
       if (merged_abspath)
         {
           if (*external_failed)
             {
               *option_id = svn_client_conflict_option_postpone;
-              return SVN_NO_ERROR;
+              break;
             }
 
           err = svn_cmdline__edit_file_externally(merged_abspath,
@@ -1360,7 +1888,7 @@ conflict_func_interactive(svn_client_con
           else if (err)
             return svn_error_trace(err);
           *option_id = svn_client_conflict_option_merged_text;
-          return SVN_NO_ERROR;
+          break;
         }
       /* else, fall through to prompting. */
       break;
@@ -1373,7 +1901,7 @@ conflict_func_interactive(svn_client_con
           if (*external_failed)
             {
               *option_id = svn_client_conflict_option_postpone;
-              return SVN_NO_ERROR;
+              break;
             }
 
           local_abspath = svn_client_conflict_get_local_abspath(conflict);
@@ -1404,12 +1932,55 @@ conflict_func_interactive(svn_client_con
             *option_id = svn_client_conflict_option_postpone;
           else
             *option_id = svn_client_conflict_option_merged_text;
-          return SVN_NO_ERROR;
+          break;
         }
       /* else, fall through to prompting. */
       break;
     }
 
+  if (*option_id != svn_client_conflict_option_unspecified &&
+      *option_id != svn_client_conflict_option_postpone)
+    {
+      SVN_ERR(mark_conflict_resolved(conflict, *option_id,
+                                     text_conflicted,
+                                     props_conflicted->nelts >  0 ? "" : NULL,
+                                     tree_conflicted,
+                                     path_prefix, conflict_stats,
+                                     ctx, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+resolve_conflict_interactively(svn_boolean_t *resolved,
+                               svn_boolean_t *postponed,
+                               svn_boolean_t *quit,
+                               svn_boolean_t *external_failed,
+                               svn_boolean_t *printed_summary,
+                               svn_boolean_t *printed_description,
+                               svn_client_conflict_t *conflict,
+                               const char *editor_cmd,
+                               apr_hash_t *config,
+                               const char *path_prefix,
+                               svn_cmdline_prompt_baton_t *pb,
+                               svn_cl__conflict_stats_t *conflict_stats,
+                               svn_client_ctx_t *ctx,
+                               apr_pool_t *result_pool,
+                               apr_pool_t *scratch_pool)
+{
+  svn_boolean_t text_conflicted;
+  apr_array_header_t *props_conflicted;
+  svn_boolean_t tree_conflicted;
+  const svn_string_t *merged_propval;
+
+  SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+                                             &props_conflicted,
+                                             &tree_conflicted,
+                                             conflict,
+                                             scratch_pool,
+                                             scratch_pool));
+
   /* Print a summary of conflicts before starting interactive resolution */
   if (! *printed_summary)
     {
@@ -1417,126 +1988,24 @@ conflict_func_interactive(svn_client_con
       *printed_summary = TRUE;
     }
 
-  /* We're in interactive mode and either the user gave no --accept
-     option or the option did not apply; let's prompt. */
-
-  /* Handle the most common cases, which is either:
-
-     Conflicting edits on a file's text, or
-     Conflicting edits on a property.
-  */
-  if (((svn_client_conflict_get_kind(conflict) == svn_wc_conflict_kind_text)
+  *resolved = FALSE;
+  if (text_conflicted
        && (svn_client_conflict_get_incoming_change(conflict) ==
            svn_wc_conflict_action_edit)
        && (svn_client_conflict_get_local_change(conflict) ==
-           svn_wc_conflict_reason_edited)))
-    SVN_ERR(handle_text_conflict(option_id, save_merged, accept_which,
-                                 quit, conflict, path_prefix, pb,
-                                 editor_cmd, config, scratch_pool));
-  else if (svn_client_conflict_get_kind(conflict) ==
-           svn_wc_conflict_kind_property)
-    SVN_ERR(handle_prop_conflict(option_id, merged_propval, accept_which,
-                                 quit, path_prefix, pb,
-                                 editor_cmd, config, conflict,
-                                 result_pool, scratch_pool));
-  else if (svn_client_conflict_get_kind(conflict) == svn_wc_conflict_kind_tree)
-    SVN_ERR(handle_tree_conflict(option_id, accept_which, quit,
-                                 conflict, path_prefix, pb,
+           svn_wc_conflict_reason_edited))
+    SVN_ERR(handle_text_conflict(resolved, postponed, quit, 
printed_description,
+                                 conflict, path_prefix, pb, editor_cmd, config,
+                                 conflict_stats, ctx, scratch_pool));
+  if (props_conflicted->nelts > 0)
+    SVN_ERR(handle_prop_conflicts(resolved, postponed, quit, &merged_propval,
+                                  path_prefix, pb, editor_cmd, config, 
conflict,
+                                  conflict_stats, ctx, result_pool, 
scratch_pool));
+  if (tree_conflicted)
+    SVN_ERR(handle_tree_conflict(resolved, postponed, quit, 
printed_description,
+                                 conflict, path_prefix, pb, conflict_stats, 
ctx,
                                  scratch_pool));
 
-  else /* other types of conflicts -- do nothing about them. */
-    {
-      *option_id = svn_client_conflict_option_postpone;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*
- * Return a legacy conflict choice corresponding to OPTION_ID.
- * Return svn_wc_conflict_choose_undefined if no corresponding
- * legacy conflict choice exists.
- */
-static svn_wc_conflict_choice_t
-conflict_option_id_to_wc_conflict_choice(
-  svn_client_conflict_option_id_t option_id)
-{
-
-  switch (option_id)
-    {
-      case svn_client_conflict_option_undefined:
-        return svn_wc_conflict_choose_undefined;
-
-      case svn_client_conflict_option_postpone:
-        return svn_wc_conflict_choose_postpone;
-
-      case svn_client_conflict_option_base_text:
-        return svn_wc_conflict_choose_base;
-
-      case svn_client_conflict_option_incoming_text:
-        return svn_wc_conflict_choose_theirs_full;
-
-      case svn_client_conflict_option_working_text:
-        return svn_wc_conflict_choose_mine_full;
-
-      case svn_client_conflict_option_incoming_text_where_conflicted:
-        return svn_wc_conflict_choose_theirs_conflict;
-
-      case svn_client_conflict_option_working_text_where_conflicted:
-        return svn_wc_conflict_choose_mine_conflict;
-
-      case svn_client_conflict_option_merged_text:
-        return svn_wc_conflict_choose_merged;
-
-      case svn_client_conflict_option_unspecified:
-        return svn_wc_conflict_choose_unspecified;
-
-      default:
-        break;
-    }
-
-  return svn_wc_conflict_choose_undefined;
-}
-
-svn_error_t *
-svn_cl__conflict_func_interactive(svn_wc_conflict_result_t **result,
-                                  const svn_wc_conflict_description2_t *desc,
-                                  void *baton,
-                                  apr_pool_t *result_pool,
-                                  apr_pool_t *scratch_pool)
-{
-  svn_cl__interactive_conflict_baton_t *b = baton;
-  svn_client_conflict_t *conflict;
-  svn_client_conflict_option_id_t option_id;
-  svn_boolean_t save_merged = FALSE;
-  const svn_string_t *merged_propval = NULL;
-
-  SVN_ERR(svn_client_conflict_from_wc_description2_t(&conflict, desc,
-                                                     scratch_pool,
-                                                     scratch_pool));
-  *result = svn_wc_create_conflict_result(svn_client_conflict_option_postpone,
-                                          NULL, result_pool);
-  SVN_ERR(conflict_func_interactive(&option_id, &save_merged, &merged_propval,
-                                    &b->accept_which, &b->quit,
-                                    &b->external_failed, &b->printed_summary,
-                                    conflict, b->editor_cmd, b->config,
-                                    b->path_prefix, b->pb, b->conflict_stats,
-                                    result_pool, scratch_pool));
-  (*result)->choice = conflict_option_id_to_wc_conflict_choice(option_id);
-  (*result)->save_merged = save_merged;
-  (*result)->merged_value = merged_propval;
-
-  /* If we are resolving a conflict, adjust the summary of conflicts. */
-  if (option_id != svn_client_conflict_option_postpone)
-    {
-      const char *local_path
-        = svn_cl__local_style_skip_ancestor(
-            b->path_prefix, svn_client_conflict_get_local_abspath(conflict),
-            scratch_pool);
-
-      svn_cl__conflict_stats_resolved(b->conflict_stats, local_path,
-                                      svn_client_conflict_get_kind(conflict));
-    }
   return SVN_NO_ERROR;
 }
 
@@ -1556,57 +2025,76 @@ svn_cl__resolve_conflict(svn_boolean_t *
                          svn_client_ctx_t *ctx,
                          apr_pool_t *scratch_pool)
 {
+  svn_boolean_t text_conflicted;
+  apr_array_header_t *props_conflicted;
+  svn_boolean_t tree_conflicted;
+
+  SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+                                             &props_conflicted,
+                                             &tree_conflicted,
+                                             conflict,
+                                             scratch_pool,
+                                             scratch_pool));
+
+  /* Resolve the conflict by --accept option or interactively if no
+   * resolution option was passed. */
   if (option_id == svn_client_conflict_option_unspecified)
-    SVN_ERR(conflict_func_interactive(&option_id, NULL, NULL,
-                                      accept_which, quit,
-                                      external_failed, printed_summary,
-                                      conflict, editor_cmd, config,
-                                      path_prefix, pb, conflict_stats,
-                                      scratch_pool, scratch_pool));
+    {
+      SVN_ERR(resolve_conflict_by_accept_option(&option_id, *accept_which,
+                                                external_failed, conflict,
+                                                editor_cmd, config,
+                                                path_prefix, pb,
+                                                conflict_stats, ctx,
+                                                scratch_pool, scratch_pool));
 
-  SVN_ERR_ASSERT(option_id != svn_client_conflict_option_unspecified);
+      if (option_id == svn_client_conflict_option_unspecified)
+        {
+          svn_boolean_t postponed = FALSE;
+          svn_boolean_t printed_description = FALSE;
+          svn_error_t *err;
 
-  /* If we are resolving a conflict, adjust the summary of conflicts. */
-  if (option_id != svn_client_conflict_option_postpone)
-    {
-      const char *local_relpath
-        = svn_cl__local_style_skip_ancestor(
-            path_prefix, svn_client_conflict_get_local_abspath(conflict),
-            scratch_pool);
-      svn_wc_conflict_kind_t conflict_kind;
-      const char *local_abspath;
-      const char *lock_abspath;
-      svn_error_t *err;
+          *quit = FALSE;
 
-      local_abspath = svn_client_conflict_get_local_abspath(conflict);
+          /* We're in interactive mode and either the user gave no --accept
+             option or the option did not apply; let's prompt. */
+          while (!*resolved && !postponed && !*quit)
+            {
+              err = resolve_conflict_interactively(resolved, &postponed, quit,
+                                                   external_failed,
+                                                   printed_summary,
+                                                   &printed_description,
+                                                   conflict,
+                                                   editor_cmd, config,
+                                                   path_prefix, pb,
+                                                   conflict_stats, ctx,
+                                                   scratch_pool, scratch_pool);
+              if (err && err->apr_err == SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE)
+                {
+                  /* Conflict resolution has failed. Let the user try again.
+                   * It is always possible to break out of this loop with
+                   * the 'quit' or 'postpone' options. */
+                  svn_handle_warning2(stderr, err, "svn: ");
+                  svn_error_clear(err);
+                  err = SVN_NO_ERROR;
+                }
+              SVN_ERR(err);
+            }
+        }
 
-      /* ### for now, resolve conflict using legacy API */
-      SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath,
-                                                     ctx->wc_ctx,
-                                                     local_abspath,
-                                                     scratch_pool,
-                                                     scratch_pool));
-      conflict_kind = svn_client_conflict_get_kind(conflict);
+      return SVN_NO_ERROR;
+    }
 
-      err = svn_wc_resolved_conflict5(
-              ctx->wc_ctx, local_abspath, svn_depth_empty, /* ??? */
-              conflict_kind == svn_wc_conflict_kind_text,
-              conflict_kind == svn_wc_conflict_kind_property ? "" : NULL,
-              conflict_kind == svn_wc_conflict_kind_tree,
-              conflict_option_id_to_wc_conflict_choice(option_id),
-              ctx->cancel_func, ctx->cancel_baton,
-              ctx->notify_func2, ctx->notify_baton2,
-              scratch_pool);
-
-      err = svn_error_compose_create(err, 
svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                     
lock_abspath,
-                                                                     
scratch_pool));
-      svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
+  /* Non-interactive resolution. */
+  SVN_ERR_ASSERT(option_id != svn_client_conflict_option_unspecified);
 
-      SVN_ERR(err);
-    
-      svn_cl__conflict_stats_resolved(conflict_stats, local_relpath,
-                                      svn_client_conflict_get_kind(conflict));
+  if (option_id != svn_client_conflict_option_postpone)
+    {
+      SVN_ERR(mark_conflict_resolved(conflict, option_id,
+                                     text_conflicted,
+                                     props_conflicted->nelts > 0 ? "" : NULL,
+                                     tree_conflicted,
+                                     path_prefix, conflict_stats,
+                                     ctx, scratch_pool));
       *resolved = TRUE;
     }
   else


Reply via email to