* subversion/libsvn_wc/merge.c

  (svn_wc_merge): If there was no external merge command specified, use
    the internal diff lib to do the merging.  Else, fall back to
    svn_io_run_diff3.


* subversion/clients/cmdline/cl.h

  (svn_cl__longopt_t, svn_cl__opt_state_t): Add merge-cmd option and
    placeholder for argument.


* subversion/clients/cmdline/main.c

  (svn_cl__option, svn_cl__cmd_table): Add 'diff3-cmd' (merge).
  
  (main): Add 'diff-cmd' (merge) processing.  Also override the value of
    diff3-cmd in the config if it was passed on the commandline.


* subversion/tests/clients/cmdline/getopt_tests_data/svn_help_log_switch_stdout

  Update expected output to current output.


git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/issue-405-internal-diff@844983 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/clients/cmdline/cl.h b/subversion/clients/cmdline/cl.h
index c1a2d46..cf8bb51 100644
--- a/subversion/clients/cmdline/cl.h
+++ b/subversion/clients/cmdline/cl.h
@@ -60,7 +60,8 @@
   svn_cl__no_diff_deleted,
   svn_cl__dry_run_opt,
   svn_cl__revprop_opt,
-  svn_cl__diff_cmd_opt
+  svn_cl__diff_cmd_opt,
+  svn_cl__merge_cmd_opt
 } svn_cl__longopt_t;
 
 
@@ -105,6 +106,7 @@
   svn_boolean_t dry_run;         /* try operation but make no changes */
   svn_boolean_t revprop;         /* operate on a revision property */
   const char *diff_cmd;          /* the external diff command to use */
+  const char *merge_cmd;         /* the external merge command to use */
 } svn_cl__opt_state_t;
 
 
diff --git a/subversion/clients/cmdline/main.c b/subversion/clients/cmdline/main.c
index 2a45197..4bd0a41 100644
--- a/subversion/clients/cmdline/main.c
+++ b/subversion/clients/cmdline/main.c
@@ -100,6 +100,8 @@
                        "do not print differences for deleted files"},
     {"diff-cmd",      svn_cl__diff_cmd_opt, 1,
                       "Use \"ARG\" as diff command"},
+    {"diff3-cmd",     svn_cl__merge_cmd_opt, 1,
+                      "Use \"ARG\" as merge command"},
 
     /* ### Perhaps the option should be named "--rev-prop" instead?
            Generally, we do include the hyphen; the only reason not to
@@ -299,7 +301,8 @@
     "  If omitted, a default value of '.' is assumed.\n\n",
     {'r', 'N', 'q', svn_cl__force_opt, svn_cl__dry_run_opt,
      svn_cl__auth_username_opt, svn_cl__auth_password_opt,
-     svn_cl__no_auth_cache_opt, svn_cl__non_interactive_opt} },
+     svn_cl__no_auth_cache_opt, svn_cl__non_interactive_opt,
+     svn_cl__merge_cmd_opt } },
   
   { "mkdir", svn_cl__mkdir, {0},
     "Create a new directory under revision control.\n"
@@ -477,7 +480,7 @@
     "  Note:  this is the way to move a working copy to a new branch.\n",
     { 'r', 'N', 'q', svn_cl__auth_username_opt,
       svn_cl__auth_password_opt, svn_cl__no_auth_cache_opt,
-      svn_cl__non_interactive_opt } },
+      svn_cl__non_interactive_opt, svn_cl__merge_cmd_opt } },
  
   { "update", svn_cl__update, {"up"}, 
     "Bring changes from the repository into the working copy.\n"
@@ -498,7 +501,7 @@
     "  while updates to the file's props are shown in the second column.\n",
     {'r', 'N', 'q', svn_cl__auth_username_opt,
      svn_cl__auth_password_opt, svn_cl__no_auth_cache_opt,
-     svn_cl__non_interactive_opt } },
+     svn_cl__non_interactive_opt, svn_cl__merge_cmd_opt } },
 
   { NULL, NULL, {0}, NULL, {0} }
 };
@@ -747,6 +750,9 @@
       case svn_cl__diff_cmd_opt:
         opt_state.diff_cmd = apr_pstrdup (pool, opt_arg);
 	break;
+      case svn_cl__merge_cmd_opt:
+        opt_state.merge_cmd = apr_pstrdup (pool, opt_arg);
+	break;
       default:
         /* Hmmm. Perhaps this would be a good place to squirrel away
            opts that commands like svn diff might need. Hmmm indeed. */
@@ -929,8 +935,12 @@
   /* XXX: Only diff_cmd for now, overlay rest later and stop passing
      opt_state altogether? */
   if (opt_state.diff_cmd)
-    svn_config_set (cfg, "helpers", "diff-cmd", opt_state.diff_cmd);
- 
+    svn_config_set (cfg, SVN_CONFIG_SECTION_HELPERS,
+                    SVN_CONFIG_OPTION_DIFF_CMD, opt_state.diff_cmd);
+  if (opt_state.merge_cmd)
+    svn_config_set (cfg, SVN_CONFIG_SECTION_HELPERS,
+                    SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd);
+
   ctx.log_msg_func = svn_cl__get_log_message;
   ctx.log_msg_baton = svn_cl__make_log_msg_baton (&opt_state, NULL, 
                                                   ctx.config, pool);
diff --git a/subversion/libsvn_wc/merge.c b/subversion/libsvn_wc/merge.c
index d5cd423..fa724cf 100644
--- a/subversion/libsvn_wc/merge.c
+++ b/subversion/libsvn_wc/merge.c
@@ -20,6 +20,7 @@
 
 #include "svn_wc.h"
 #include "svn_diff.h"
+#include "svn_config.h"
 #include "wc.h"
 #include "entries.h"
 #include "translate.h"
@@ -49,10 +50,8 @@
   const char *eol;
   apr_status_t apr_err;
   const svn_wc_entry_t *entry;
-  svn_diff_t *diff;
-  const char *target_marker;
-  const char *left_marker;
-  const char *right_marker;
+  const char *merge_cmd = NULL;
+  svn_boolean_t contains_conflicts;
 
   svn_path_split (merge_target, &mt_pt, &mt_bn, pool);
 
@@ -126,32 +125,67 @@
       SVN_ERR (svn_io_copy_file (left, tmp_left, TRUE, pool));
       SVN_ERR (svn_io_copy_file (right, tmp_right, TRUE, pool));
 
-      /* Do the Deed, using all four scratch files. */
-      SVN_ERR (svn_diff3_file (&diff, tmp_left, tmp_target, tmp_right, pool));
+      /* Find out if we need to run an external merge */
+      if (config)
+        {
+          svn_config_t *cfg = apr_hash_get (config,
+                                            SVN_CONFIG_CATEGORY_CONFIG,
+                                            APR_HASH_KEY_STRING);
+          svn_config_get (cfg, &merge_cmd, SVN_CONFIG_SECTION_HELPERS,
+	                  SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
+        }
 
-      /* Labels fall back to sensible defaults if not specified. */
-      if (target_label)
-        target_marker = apr_psprintf(pool, "<<<<<<< %s", target_label);
+      if (merge_cmd)
+        {
+          int exit_code;
+
+          SVN_ERR (svn_io_run_diff3 (".",
+                                     tmp_target, tmp_left, tmp_right,
+                                     target_label, left_label, right_label,
+                                     result_f, &exit_code, config,
+                                     pool));
+          
+          contains_conflicts = exit_code == 1;
+        }
       else
-        target_marker = "<<<<<<< .working";
+        {
+          svn_diff_t *diff;
+          const char *target_marker;
+          const char *left_marker;
+          const char *right_marker;
 
-      if (left_label)
-        left_marker = apr_psprintf(pool, "||||||| %s", left_label);
-      else
-        left_marker = "||||||| .old";
+          SVN_ERR (svn_diff3_file (&diff,
+                                   tmp_left, tmp_target, tmp_right,
+                                   pool));
 
-      if (right_label)
-        right_marker = apr_psprintf(pool, ">>>>>>> %s", right_label);
-      else
-        right_marker = ">>>>>>> .new";
+          /* Labels fall back to sensible defaults if not specified. */
+          if (target_label)
+            target_marker = apr_psprintf (pool, "<<<<<<< %s", target_label);
+          else
+            target_marker = "<<<<<<< .working";
 
-      SVN_ERR (svn_diff3_file_output (result_f, diff,
-                                      tmp_left, tmp_target, tmp_right,
-                                      left_marker, target_marker, right_marker,
-                                      "=======", /* seperator */
-                                      FALSE, /* display original in conflict */
-                                      FALSE, /* try to resolve conflicts */
-                                      pool));
+          if (left_label)
+            left_marker = apr_psprintf (pool, "||||||| %s", left_label);
+          else
+            left_marker = "||||||| .old";
+
+          if (right_label)
+            right_marker = apr_psprintf (pool, ">>>>>>> %s", right_label);
+          else
+            right_marker = ">>>>>>> .new";
+
+          SVN_ERR (svn_diff3_file_output (result_f, diff,
+                                          tmp_left, tmp_target, tmp_right,
+                                          left_marker,
+                                          target_marker,
+                                          right_marker,
+                                          "=======", /* seperator */
+                                          FALSE, /* display original */
+                                          FALSE, /* resolve conflicts */
+                                          pool));
+
+          contains_conflicts = svn_diff_contains_conflicts (diff);
+        }
 
       /* Close the output file */
       apr_err = apr_file_close (result_f);
@@ -160,7 +194,7 @@
           (apr_err, NULL,
            "svn_wc_merge: unable to close tmp file `%s'", result_target);
 
-      if (svn_diff_contains_conflicts (diff) && ! dry_run)  /* got a conflict */
+      if (contains_conflicts && ! dry_run)  /* got a conflict */
         {
           /* Preserve the three pre-merge files, and modify the
              entry (mark as conflicted, track the preserved files). */ 
@@ -274,7 +308,7 @@
           *merge_outcome = svn_wc_merge_conflict;
 
         }
-      else if (svn_diff_contains_conflicts (diff) && dry_run)
+      else if (contains_conflicts && dry_run)
         {
           *merge_outcome = svn_wc_merge_conflict;
         } /* end of conflict handling */
diff --git a/subversion/tests/clients/cmdline/getopt_tests_data/svn_help_log_switch_stdout b/subversion/tests/clients/cmdline/getopt_tests_data/svn_help_log_switch_stdout
index 09ab6da..088c72e 100644
--- a/subversion/tests/clients/cmdline/getopt_tests_data/svn_help_log_switch_stdout
+++ b/subversion/tests/clients/cmdline/getopt_tests_data/svn_help_log_switch_stdout
@@ -52,4 +52,5 @@
   --password arg           : specify a password ARG
   --no-auth-cache          : do not cache authentication tokens
   --non-interactive        : do no interactive prompting
+  --diff3-cmd arg          : Use "ARG" as merge command