Partially fix issue #4316 "Merge errors out after resolving conflicts", by
repeating the merge while there is more to do if all conflicts resulting
from one part of the merge are then resolved interactively.

### This currently just repeats the merge with the same parameters, so it
won't work properly for non-merge-tracking merges, because it will repeat
the earlier parts of the merge, and that will often produce wrong results
including more conflicts.  There is not yet a simple way to determine either
whether the merge used merge-tracking, or what the remaining revision
range(s) or parameters should be.

* subversion/svn/merge-cmd.c
  (run_merge): Document about returning early when there are conflicts.
  (svn_cl__merge): Repeat the merge every time it returns early with
    conflicts if all conflicts are then resolved.
--This line, and those below, will be ignored--

Index: subversion/svn/merge-cmd.c
===================================================================
--- subversion/svn/merge-cmd.c	(revision 1446794)
+++ subversion/svn/merge-cmd.c	(working copy)
@@ -146,6 +146,18 @@ automatic_merge(const char *source_path_
  *
  * Having FIRST_RANGE_START/_END params is ugly -- we should be able to use
  * PEG_REVISION1/2 and/or RANGES_TO_MERGE instead, maybe adjusting the caller.
+ *
+ * If the merge involves multiple revision ranges (either specified
+ * externally or calculated internally) and it raises any conflicts while
+ * merging a range other than the last, return a SVN_ERR_WC_FOUND_CONFLICT
+ * error.
+ *
+ * In that case, the caller may resolve all the conflicts and then call
+ * this again with appropriate parameters to resume the merge where it
+ * left off.  For a merge-tracking merge, using the same parameters again
+ * should work.  (In principle, if the caller knows which of the conflicts
+ * might get in the way of the rest of the merge, it need only resolve
+ * those.)
  */
 static svn_error_t *
 run_merge(svn_boolean_t two_sources_specified,
@@ -277,6 +289,7 @@ svn_cl__merge(apr_getopt_t *os,
     peg_revision2;
   apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges;
   svn_boolean_t has_explicit_target = FALSE;
+  svn_boolean_t merge_again;
 
   /* Merge doesn't support specifying a revision or revision range
      when using --reintegrate. */
@@ -543,62 +556,101 @@ svn_cl__merge(apr_getopt_t *os,
                                   "with --reintegrate"));
     }
 
-  /* Decide how to handle conflicts.  If the user wants interactive
-   * conflict resolution, postpone conflict resolution during the merge
-   * and if any conflicts occur we'll run the conflict resolver later.
-   * Otherwise install the appropriate resolver now. */
-  if (opt_state->accept_which == svn_cl__accept_unspecified
-      || opt_state->accept_which == svn_cl__accept_postpone
-      || opt_state->accept_which == svn_cl__accept_edit
-      || opt_state->accept_which == svn_cl__accept_launch)
-    {
-      /* 'svn.c' has already installed the 'postpone' handler for us. */
-    }
-  else
-    {
-      svn_cl__interactive_conflict_baton_t *b;
+  /* Do the merge.
+   *
+   * After running the merge, if there are conflicts and the user resolves
+   * them all (interactively) and it was not the final revision range of
+   * the merge, then repeat the merge until it is all done.  For a merge-
+   * tracking merge, repeating it with the same arguments should work;
+   * otherwise, we have to move on to the next revision range.
+   *
+   * ### At the moment, we always use the same arguments again, which will
+   *     be bad for a non-merge-tracking merge.
+   */
+  do
+    {
+      svn_cl__notifier_reset_conflict_stats(ctx->notify_baton2);
+
+      /* Decide how to handle conflicts.  If the user wants interactive
+       * conflict resolution, postpone conflict resolution during the merge
+       * and if any conflicts occur we'll run the conflict resolver later.
+       * Otherwise install the appropriate resolver now. */
+      if (opt_state->accept_which == svn_cl__accept_unspecified
+          || opt_state->accept_which == svn_cl__accept_postpone
+          || opt_state->accept_which == svn_cl__accept_edit
+          || opt_state->accept_which == svn_cl__accept_launch)
+        {
+          /* Postpone conflicts, and start counting them. */
+          /* ### 'svn.c' already installed this but we must reset it */
+          ctx->conflict_func2 = svn_cl__conflict_func_postpone;
+          ctx->conflict_baton2 = svn_cl__get_conflict_func_postpone_baton(pool);
+        }
+      else
+        {
+          svn_cl__interactive_conflict_baton_t *b;
 
-      ctx->conflict_func2 = svn_cl__conflict_func_interactive;
-      SVN_ERR(svn_cl__get_conflict_func_interactive_baton(
-                &b,
-                opt_state->accept_which,
-                ctx->config, opt_state->editor_cmd,
-                ctx->cancel_func, ctx->cancel_baton, pool));
-      ctx->conflict_baton2 = b;
-    }
-
-  merge_err = run_merge(two_sources_specified,
-                        sourcepath1, peg_revision1,
-                        sourcepath2, peg_revision2,
-                        targetpath,
-                        ranges_to_merge, first_range_start, first_range_end,
-                        opt_state, options, ctx, pool);
-  if (merge_err && merge_err->apr_err
-                   == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
-    {
-      return svn_error_quick_wrap(
-               merge_err,
-               _("Merge tracking not possible, use --ignore-ancestry or\n"
-                 "fix invalid mergeinfo in target with 'svn propset'"));
-    }
-  if (! merge_err || merge_err->apr_err == SVN_ERR_WC_FOUND_CONFLICT)
-    {
-      svn_error_t *err = SVN_NO_ERROR;
+          ctx->conflict_func2 = svn_cl__conflict_func_interactive;
+          SVN_ERR(svn_cl__get_conflict_func_interactive_baton(
+                    &b,
+                    opt_state->accept_which,
+                    ctx->config, opt_state->editor_cmd,
+                    ctx->cancel_func, ctx->cancel_baton, pool));
+          ctx->conflict_baton2 = b;
+        }
+
+      merge_err = run_merge(two_sources_specified,
+                            sourcepath1, peg_revision1,
+                            sourcepath2, peg_revision2,
+                            targetpath,
+                            ranges_to_merge, first_range_start, first_range_end,
+                            opt_state, options, ctx, pool);
+      if (merge_err && merge_err->apr_err
+                       == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
+        {
+          return svn_error_quick_wrap(
+                   merge_err,
+                   _("Merge tracking not possible, use --ignore-ancestry or\n"
+                     "fix invalid mergeinfo in target with 'svn propset'"));
+        }
+      merge_again = FALSE;
+      if (! merge_err || merge_err->apr_err == SVN_ERR_WC_FOUND_CONFLICT)
+        {
+          svn_error_t *err = SVN_NO_ERROR;
 
-      if (! opt_state->quiet)
-        err = svn_cl__notifier_print_conflict_stats(ctx->notify_baton2,
+          if (! opt_state->quiet)
+            err = svn_cl__notifier_print_conflict_stats(ctx->notify_baton2,
                                                         pool);
 
-      /* Resolve any postponed conflicts.  (Only if we've been using the
-       * default 'postpone' resolver which remembers what was postponed.) */
-      if (!err && ctx->conflict_func2 == svn_cl__conflict_func_postpone)
-        err = svn_cl__resolve_postponed_conflicts(NULL,
-                                                  ctx->conflict_baton2,
-                                                  opt_state->accept_which,
-                                                  opt_state->editor_cmd,
-                                                  ctx, pool);
-      merge_err = svn_error_compose_create(merge_err, err);
+          /* Resolve any postponed conflicts interactively.  (Only if
+           * we've been using the default 'postpone' resolver which
+           * remembers what was postponed, and want to do some kind of
+           * interactive resolution.) */
+          if (!err && ctx->conflict_func2 == svn_cl__conflict_func_postpone
+              && opt_state->accept_which != svn_cl__accept_postpone
+              && ! opt_state->dry_run)
+            {
+              svn_boolean_t resolved;
+
+              err = svn_cl__resolve_postponed_conflicts(&resolved,
+                                                        ctx->conflict_baton2,
+                                                        opt_state->accept_which,
+                                                        opt_state->editor_cmd,
+                                                        ctx, pool);
+              /* Decide whether to merge again -- and, if so, with what
+               * parameters.  MERGE_ERR not null means there were some
+               * conflicts and there is more merging to do. */
+              if (merge_err && resolved)
+                {
+                  merge_again = TRUE;
+                  svn_error_clear(merge_err);
+                  merge_err = SVN_NO_ERROR;
+                }
+            }
+          merge_err = svn_error_compose_create(merge_err, err);
+        }
     }
+  /* if all resolved and more ranges to do, then go around, else don't */
+  while (merge_again && ! merge_err);
 
   return svn_cl__may_need_force(merge_err);
 }
