You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by gb...@apache.org on 2013/11/09 14:18:15 UTC

svn commit: r1540299 - in /subversion/branches/invoke-diff-merge-feature: ./ subversion/include/ subversion/libsvn_client/ subversion/libsvn_subr/ subversion/libsvn_wc/ subversion/svn/

Author: gbg
Date: Sat Nov  9 13:18:14 2013
New Revision: 1540299

URL: http://svn.apache.org/r1540299
Log:
On the invoke-diff-cmd-feature branch: Checkpoint commit (ignore, trivial).


Modified:
    subversion/branches/invoke-diff-merge-feature/BRANCH-README
    subversion/branches/invoke-diff-merge-feature/subversion/include/svn_config.h
    subversion/branches/invoke-diff-merge-feature/subversion/include/svn_io.h
    subversion/branches/invoke-diff-merge-feature/subversion/include/svn_wc.h
    subversion/branches/invoke-diff-merge-feature/subversion/libsvn_client/merge.c
    subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/config_file.c
    subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/io.c
    subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/deprecated.c
    subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/merge.c
    subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/wc.h
    subversion/branches/invoke-diff-merge-feature/subversion/svn/cl.h
    subversion/branches/invoke-diff-merge-feature/subversion/svn/conflict-callbacks.c
    subversion/branches/invoke-diff-merge-feature/subversion/svn/svn.c
    subversion/branches/invoke-diff-merge-feature/subversion/svn/util.c

Modified: subversion/branches/invoke-diff-merge-feature/BRANCH-README
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/BRANCH-README?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/BRANCH-README (original)
+++ subversion/branches/invoke-diff-merge-feature/BRANCH-README Sat Nov  9 13:18:14 2013
@@ -43,11 +43,36 @@ TBD
 UI components
 -------------
 
+#  --invoke-diff-cmd and its user interface components for the command
+#  line have been installed everywhere where --diff-cmd is available,
+#  in svnlook.c, svn.c, svnlog.c.
+
+& Users can specify the following mutually exclusive options:
+
+  --invoke-merge-cmd, --diff3-cmd , and also define what diff3 program
+  to use via the ./svn/config file, using the new config file
+  parameter "invoke-merge-cmd"..
+
+  The selection first defaults to the config file entry (if defined)
+  which in turn is overridden when one of --invoke-merge-cmd,
+  --diff-cmd are invoked.
+
 
 Changes to the existing code structure:
 =======================================
 
-TBD
+None.
+
+The existing --diff3-cmd has been in use for years and is best left
+untouched, since many users will not require the extra services
+offered by the --invoke-diff3-cmd feature, and any errors could cause
+many users a lot of problems.  
+
+Moreover, the nature of --invoke-diff3-cmd and --diff-cmd are quite
+different; --diff-3 is tailored for GNU diff3 and carefully checks the
+user's input of switches, whereas --invoke-diff3-cmd is a
+free-style-anything-goes-including-shooting-your-foot creation.
+
 
 Tests
 =====
@@ -80,12 +105,7 @@ kdiff3: 0.9.97 (32 bit)
 TODO:
 ====
 
-  * add invoke-merge-cmd to svn.c
-    # plug in opt_get_invoke-merge-cmd
-    # UI checks:
-      write decision matrix
-      write checking code
-    # write help info
+  * write help info
 
   * Create the UI for the interactive dialogue in
     subversion/svn/conflict-callbacks.
@@ -97,11 +117,8 @@ TODO:
     (3i) - interactively select diff3 tool now to resolve conflict  
 
   * make list of where else in svn to add the invoke-merge-cmd.
+   - svn cleanup
 
-  * add routing for invoke-merge-cmd.
-
-  * add new function to io.c that copies svn_io_run_diff3_3().
-  
   * config file service:
     plug in config file service into io.c
     add new entry in config file

Modified: subversion/branches/invoke-diff-merge-feature/subversion/include/svn_config.h
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/include/svn_config.h?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/include/svn_config.h (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/include/svn_config.h Sat Nov  9 13:18:14 2013
@@ -116,6 +116,8 @@ typedef struct svn_config_t svn_config_t
 #define SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG     "diff3-has-program-arg"
 /** @since New in 1.9. */
 #define SVN_CONFIG_OPTION_INVOKE_DIFF_CMD           "invoke-diff-cmd"
+/** @since New in 1.9. */
+#define SVN_CONFIG_OPTION_INVOKE_MERGE_CMD           "invoke-merge-cmd"
 #define SVN_CONFIG_OPTION_MERGE_TOOL_CMD            "merge-tool-cmd"
 #define SVN_CONFIG_SECTION_MISCELLANY           "miscellany"
 #define SVN_CONFIG_OPTION_GLOBAL_IGNORES            "global-ignores"

Modified: subversion/branches/invoke-diff-merge-feature/subversion/include/svn_io.h
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/include/svn_io.h?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/include/svn_io.h (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/include/svn_io.h Sat Nov  9 13:18:14 2013
@@ -2426,6 +2426,25 @@ svn_io_run_external_diff(const char *dir
                          const char *external_diff_cmd,
                          apr_pool_t *scratch_pool);
 
+/** Run the external merge command defined by the invoke-diff3-cmd
+ *  option.
+ *  
+ *  @since New in 1.9.
+ */
+svn_error_t *
+svn_io_run_invoke_diff3(int *exitcode,
+                        const char *dir,
+                        const char *mine,
+                        const char *older,
+                        const char *yours,
+                        const char *mine_label,
+                        const char *older_label,
+                        const char *yours_label,
+                        apr_file_t *merged,
+                        const char *diff3_cmd,
+                        apr_pool_t *pool);
+
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/branches/invoke-diff-merge-feature/subversion/include/svn_wc.h
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/include/svn_wc.h?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/include/svn_wc.h (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/include/svn_wc.h Sat Nov  9 13:18:14 2013
@@ -7011,6 +7011,35 @@ typedef enum svn_wc_merge_outcome_t
  * @since New in 1.8.
  */
 svn_error_t *
+svn_wc_merge6(enum svn_wc_merge_outcome_t *merge_content_outcome,
+              enum svn_wc_notify_state_t *merge_props_state,
+              svn_wc_context_t *wc_ctx,
+              const char *left_abspath,
+              const char *right_abspath,
+              const char *target_abspath,
+              const char *left_label,
+              const char *right_label,
+              const char *target_label,
+              const svn_wc_conflict_version_t *left_version,
+              const svn_wc_conflict_version_t *right_version,
+              svn_boolean_t dry_run,
+              const char *diff3_cmd,
+              const char *invoke_diff3_cmd,
+              const apr_array_header_t *merge_options,
+              apr_hash_t *original_props,
+              const apr_array_header_t *prop_diff,
+              svn_wc_conflict_resolver_func2_t conflict_func,
+              void *conflict_baton,
+              svn_cancel_func_t cancel_func,
+              void *cancel_baton,
+              apr_pool_t *scratch_pool);
+
+/** Similar to svn_wc_merge6() but with @a invoke_diff3_cmd.
+ *
+ * @since New in 1.9.
+ */
+SVN_DEPRECATED
+svn_error_t *
 svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome,
               enum svn_wc_notify_state_t *merge_props_state,
               svn_wc_context_t *wc_ctx,

Modified: subversion/branches/invoke-diff-merge-feature/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/libsvn_client/merge.c?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/libsvn_client/merge.c Sat Nov  9 13:18:14 2013
@@ -316,10 +316,13 @@ typedef struct merge_cmd_baton_t {
   /* A list of tree conflict victim absolute paths which may be NULL. */
   apr_hash_t *tree_conflicted_abspaths;
 
-  /* The diff3_cmd in ctx->config, if any, else null.  We could just
-     extract this as needed, but since more than one caller uses it,
-     we just set it up when this baton is created. */
+  /* The diff3_cmd and invoke_diff3_cmd in ctx->config, if any, else
+     null.  We could just extract this as needed, but since more than
+     one caller uses it, we just set it up when this baton is
+     created. */
   const char *diff3_cmd;
+  const char *invoke_diff3_cmd;
+
   const apr_array_header_t *merge_options;
 
   /* RA sessions used throughout a merge operation.  Opened/re-parented
@@ -2040,11 +2043,13 @@ merge_file_changed(const char *relpath,
 
       /* Do property merge and text merge in one step so that keyword expansion
          takes into account the new property values. */
-      SVN_ERR(svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx,
+      SVN_ERR(svn_wc_merge6(&content_outcome, &property_state, ctx->wc_ctx,
                             left_file, right_file, local_abspath,
                             left_label, right_label, target_label,
                             left, right,
-                            merge_b->dry_run, merge_b->diff3_cmd,
+                            merge_b->dry_run, 
+                            merge_b->diff3_cmd,
+                            merge_b->invoke_diff3_cmd,
                             merge_b->merge_options,
                             left_props, prop_changes,
                             NULL, NULL,

Modified: subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/config_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/config_file.c?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/config_file.c (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/config_file.c Sat Nov  9 13:18:14 2013
@@ -1192,6 +1192,11 @@ svn_config_ensure(const char *config_dir
         "###   This will override the compile-time default, which is to use" NL
         "###   Subversion's internal diff implementation."                   NL
         "# invoke-diff-cmd = (see svn help diff for examples)"               NL
+        "### Set invoke-diff3-cmd to the absolute path of your 'diff'"       NL
+        "### program."                                                       NL
+        "###   This will override the compile-time default, which is to use" NL
+        "###   Subversion's internal merge implementation."                  NL
+        "# invoke-diff3-cmd = (see svn help merge for examples)"             NL
         "### Set merge-tool-cmd to the command used to invoke your external" NL
         "### merging tool of choice. Subversion will pass 5 arguments to"    NL
         "### the specified command: base theirs mine merged wcfile"          NL

Modified: subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/io.c?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/libsvn_subr/io.c Sat Nov  9 13:18:14 2013
@@ -3150,6 +3150,7 @@ svn_io_run_external_diff(const char *dir
   return SVN_NO_ERROR;
 }
 
+
 svn_error_t *
 svn_io_run_diff2(const char *dir,
                  const char *const *user_args,
@@ -3331,6 +3332,59 @@ svn_io_run_diff3_3(int *exitcode,
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_io_run_invoke_diff3(int *exitcode,
+                        const char *dir,
+                        const char *mine,
+                        const char *older,
+                        const char *yours,
+                        const char *mine_label,
+                        const char *older_label,
+                        const char *yours_label,
+                        apr_file_t *merged,
+                        const char *invoke_diff3_cmd,
+                        apr_pool_t *pool)
+{
+
+  const char ** cmd;
+
+  apr_pool_t *scratch_pool = svn_pool_create(pool); 
+
+  if (0 == strlen(invoke_diff3_cmd)) 
+     return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, NULL);
+
+  cmd = __create_custom_diff_cmd(mine_label, yours_label, older_label, 
+                                 mine, yours, older, 
+                                 invoke_diff3_cmd, scratch_pool);
+  
+  SVN_ERR(svn_io_run_cmd(dir, cmd[0], cmd, exitcode, NULL, TRUE,
+                         NULL, merged, NULL, scratch_pool));
+
+
+  /* According to the diff3 docs, a '0' means the merge was clean, and
+     '1' means conflict markers were found.  Anything else is real
+     error. */
+  if ((*exitcode != 0) && (*exitcode != 1))
+    {
+
+      int i;
+      const char *failed_command = "";
+
+      for (i = 0; cmd[i]; ++i)
+          failed_command = apr_pstrcat(pool, failed_command, 
+                                       cmd[i], " ", (char*) NULL);
+      svn_pool_destroy(scratch_pool);
+      return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+                               _("'%s' was expanded to '%s' and returned %d"),
+                               invoke_diff3_cmd,
+                               failed_command,
+                               *exitcode);
+    }
+  else
+    svn_pool_destroy(scratch_pool);
+
+  return SVN_NO_ERROR;
+}
 
 /* Canonicalize a string for hashing.  Modifies KEY in place. */
 static APR_INLINE char *

Modified: subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/deprecated.c?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/deprecated.c (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/deprecated.c Sat Nov  9 13:18:14 2013
@@ -4402,6 +4402,53 @@ svn_wc_copy(const char *src_path,
 /*** From merge.c ***/
 
 svn_error_t *
+svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome,
+              enum svn_wc_notify_state_t *merge_props_outcome,
+              svn_wc_context_t *wc_ctx,
+              const char *left_abspath,
+              const char *right_abspath,
+              const char *target_abspath,
+              const char *left_label,
+              const char *right_label,
+              const char *target_label,
+              const svn_wc_conflict_version_t *left_version,
+              const svn_wc_conflict_version_t *right_version,
+              svn_boolean_t dry_run,
+              const char *diff3_cmd,
+              const apr_array_header_t *merge_options,
+              apr_hash_t *original_props,
+              const apr_array_header_t *prop_diff,
+              svn_wc_conflict_resolver_func2_t conflict_func,
+              void *conflict_baton,
+              svn_cancel_func_t cancel_func,
+              void *cancel_baton,
+              apr_pool_t *scratch_pool)
+{
+  return  svn_wc_merge6(merge_content_outcome,
+                        merge_props_outcome,
+                        wc_ctx,
+                        left_abspath,
+                        right_abspath,
+                        target_abspath,
+                        left_label,
+                        right_label,
+                        target_label,
+                        left_version,
+                        right_version,
+                        dry_run,
+                        diff3_cmd,
+                        NULL,
+                        merge_options,
+                        original_props,
+                        prop_diff,
+                        conflict_func,
+                        conflict_baton,
+                        cancel_func,
+                        cancel_baton,
+                        scratch_pool);
+}
+
+svn_error_t *
 svn_wc_merge4(enum svn_wc_merge_outcome_t *merge_outcome,
               svn_wc_context_t *wc_ctx,
               const char *left_abspath,
@@ -4816,3 +4863,98 @@ svn_wc__conflict_description2_dup(const 
 
   return new_conflict;
 }
+
+/* 
+   Prepare to merge a file content change into the working copy.
+
+   This does not merge properties; see svn_wc__merge_props() for that.
+   This does not necessarily change the file TARGET_ABSPATH on disk; it
+   may instead return work items that will replace the file on disk when
+   they are run.  ### Can we be more consistent about this?
+
+   Merge the difference between LEFT_ABSPATH and RIGHT_ABSPATH into
+   TARGET_ABSPATH.
+
+   Set *WORK_ITEMS to the appropriate work queue operations.
+
+   If there are any conflicts, append a conflict description to
+   *CONFLICT_SKEL.  (First allocate *CONFLICT_SKEL from RESULT_POOL if
+   it is initially NULL.  CONFLICT_SKEL itself must not be NULL.)
+   Also, unless it is considered to be a 'binary' file, mark any
+   conflicts in the text of the file TARGET_ABSPATH using LEFT_LABEL,
+   RIGHT_LABEL and TARGET_LABEL.
+
+   Set *MERGE_OUTCOME to indicate the result.
+
+   When DRY_RUN is true, no actual changes are made to the working copy.
+
+   If DIFF3_CMD is specified, the given external diff3 tool will
+   be used instead of our built in diff3 routines.
+
+   When MERGE_OPTIONS are specified, they are used by the internal
+   diff3 routines, or passed to the external diff3 tool.
+
+   WRI_ABSPATH describes in which working copy information should be
+   retrieved. (Interesting for merging file externals).
+
+   OLD_ACTUAL_PROPS is the set of actual properties before merging; used for
+   detranslating the file before merging.  This is necessary because, in
+   the case of updating, the update can have sent new properties, so we
+   cannot simply fetch and use the current actual properties.
+
+     ### Is OLD_ACTUAL_PROPS still necessary, now that we first prepare the
+         content change and property change and then apply them both to
+         the WC together?
+
+   Property changes sent by the update are provided in PROP_DIFF.
+
+   For a complete description, see svn_wc_merge5() for which this is
+   the (loggy) implementation.
+
+   *WORK_ITEMS will be allocated in RESULT_POOL. All temporary allocations
+   will be performed in SCRATCH_POOL.
+*/
+svn_error_t *
+svn_wc__internal_merge(svn_skel_t **work_items,
+                       svn_skel_t **conflict_skel,
+                       enum svn_wc_merge_outcome_t *merge_outcome,
+                       svn_wc__db_t *db,
+                       const char *left_abspath,
+                       const char *right_abspath,
+                       const char *target_abspath,
+                       const char *wri_abspath,
+                       const char *left_label,
+                       const char *right_label,
+                       const char *target_label,
+                       apr_hash_t *old_actual_props,
+                       svn_boolean_t dry_run,
+                       const char *diff3_cmd,
+                       const apr_array_header_t *merge_options,
+                       const apr_array_header_t *prop_diff,
+                       svn_cancel_func_t cancel_func,
+                       void *cancel_baton,
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
+{
+  return svn_wc__internal_merge1(work_items,
+                                 conflict_skel,
+                                 merge_outcome,
+                                 db,
+                                 left_abspath,
+                                 right_abspath,
+                                 target_abspath,
+                                 wri_abspath,
+                                 left_label, 
+                                 right_label, 
+                                 target_label,
+                                 old_actual_props,
+                                 dry_run,
+                                 diff3_cmd,
+                                 NULL,
+                                 merge_options,
+                                 prop_diff,
+                                 cancel_func, 
+                                 cancel_baton,
+                                 result_pool, 
+                                 scratch_pool);
+}

Modified: subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/merge.c?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/merge.c (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/merge.c Sat Nov  9 13:18:14 2013
@@ -45,11 +45,12 @@ typedef struct merge_target_t
   const char *local_abspath;                /* The absolute path to target */
   const char *wri_abspath;                  /* The working copy of target */
 
-  apr_hash_t *old_actual_props;                 /* The set of actual properties
+  apr_hash_t *old_actual_props;             /* The set of actual properties
                                                before merging */
   const apr_array_header_t *prop_diff;      /* The property changes */
 
   const char *diff3_cmd;                    /* The diff3 command and options */
+  const char *invoke_diff3_cmd;             /* The invoke_diff3_cmd command  */
   const apr_array_header_t *merge_options;
 
 } merge_target_t;
@@ -437,6 +438,7 @@ static svn_error_t *
 do_text_merge_external(svn_boolean_t *contains_conflicts,
                        apr_file_t *result_f,
                        const char *diff3_cmd,
+                       const char *invoke_diff3_cmd,
                        const apr_array_header_t *merge_options,
                        const char *detranslated_target,
                        const char *left_abspath,
@@ -448,17 +450,26 @@ do_text_merge_external(svn_boolean_t *co
 {
   int exit_code;
 
-  SVN_ERR(svn_io_run_diff3_3(&exit_code, ".",
-                             detranslated_target, left_abspath, right_abspath,
-                             target_label, left_label, right_label,
-                             result_f, diff3_cmd,
-                             merge_options, scratch_pool));
-
-  *contains_conflicts = exit_code == 1;
+  if (diff3_cmd)
+    SVN_ERR(svn_io_run_diff3_3(&exit_code, ".",
+                               detranslated_target, left_abspath, right_abspath,
+                               target_label, left_label, right_label,
+                               result_f, diff3_cmd,
+                               merge_options, scratch_pool));
+  else 
+    SVN_ERR(svn_io_run_invoke_diff3(&exit_code, ".",
+                                    detranslated_target, 
+                                    left_abspath, right_abspath,
+                                    target_label, left_label, right_label,
+                                    result_f, invoke_diff3_cmd,
+                                    scratch_pool));
+  
+  *contains_conflicts = (exit_code == 1);
 
   return SVN_NO_ERROR;
 }
 
+
 /* Preserve the three pre-merge files.
 
    Create three empty files, with unique names that each include the
@@ -843,11 +854,13 @@ merge_text_file(svn_skel_t **work_items,
                                      temp_dir, base_name, ".tmp",
                                      svn_io_file_del_none, pool, pool));
 
-  /* Run the external or internal merge, as requested. */
-  if (mt->diff3_cmd)
+  /* Run the external (old-style or new-style) or internal merge, as
+     requested. */
+  if (mt->diff3_cmd || mt->invoke_diff3_cmd)
       SVN_ERR(do_text_merge_external(&contains_conflicts,
                                      result_f,
                                      mt->diff3_cmd,
+                                     mt->invoke_diff3_cmd,
                                      mt->merge_options,
                                      detranslated_target_abspath,
                                      left_abspath,
@@ -1066,7 +1079,7 @@ merge_binary_file(svn_skel_t **work_item
 }
 
 svn_error_t *
-svn_wc__internal_merge(svn_skel_t **work_items,
+svn_wc__internal_merge1(svn_skel_t **work_items,
                        svn_skel_t **conflict_skel,
                        enum svn_wc_merge_outcome_t *merge_outcome,
                        svn_wc__db_t *db,
@@ -1080,6 +1093,7 @@ svn_wc__internal_merge(svn_skel_t **work
                        apr_hash_t *old_actual_props,
                        svn_boolean_t dry_run,
                        const char *diff3_cmd,
+                       const char *invoke_diff3_cmd,
                        const apr_array_header_t *merge_options,
                        const apr_array_header_t *prop_diff,
                        svn_cancel_func_t cancel_func,
@@ -1106,6 +1120,8 @@ svn_wc__internal_merge(svn_skel_t **work
   mt.old_actual_props = old_actual_props;
   mt.prop_diff = prop_diff;
   mt.diff3_cmd = diff3_cmd;
+  mt.invoke_diff3_cmd = invoke_diff3_cmd;
+  
   mt.merge_options = merge_options;
 
   /* Decide if the merge target is a text or binary file. */
@@ -1121,7 +1137,9 @@ svn_wc__internal_merge(svn_skel_t **work
     }
 
   SVN_ERR(detranslate_wc_file(&detranslated_target_abspath, &mt,
-                              (! is_binary) && diff3_cmd != NULL,
+                              (! is_binary) 
+                              && diff3_cmd != NULL 
+                              && invoke_diff3_cmd != NULL,
                               target_abspath,
                               cancel_func, cancel_baton,
                               scratch_pool, scratch_pool));
@@ -1193,7 +1211,7 @@ svn_wc__internal_merge(svn_skel_t **work
 
 
 svn_error_t *
-svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome,
+svn_wc_merge6(enum svn_wc_merge_outcome_t *merge_content_outcome,
               enum svn_wc_notify_state_t *merge_props_outcome,
               svn_wc_context_t *wc_ctx,
               const char *left_abspath,
@@ -1206,6 +1224,7 @@ svn_wc_merge5(enum svn_wc_merge_outcome_
               const svn_wc_conflict_version_t *right_version,
               svn_boolean_t dry_run,
               const char *diff3_cmd,
+              const char *invoke_diff3_cmd,
               const apr_array_header_t *merge_options,
               apr_hash_t *original_props,
               const apr_array_header_t *prop_diff,
@@ -1333,22 +1352,23 @@ svn_wc_merge5(enum svn_wc_merge_outcome_
     }
 
   /* Merge the text. */
-  SVN_ERR(svn_wc__internal_merge(&work_items,
-                                 &conflict_skel,
-                                 merge_content_outcome,
-                                 wc_ctx->db,
-                                 left_abspath,
-                                 right_abspath,
-                                 target_abspath,
-                                 target_abspath,
-                                 left_label, right_label, target_label,
-                                 old_actual_props,
-                                 dry_run,
-                                 diff3_cmd,
-                                 merge_options,
-                                 prop_diff,
-                                 cancel_func, cancel_baton,
-                                 scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__internal_merge1(&work_items,
+                                  &conflict_skel,
+                                  merge_content_outcome,
+                                  wc_ctx->db,
+                                  left_abspath,
+                                  right_abspath,
+                                  target_abspath,
+                                  target_abspath,
+                                  left_label, right_label, target_label,
+                                  old_actual_props,
+                                  dry_run,
+                                  diff3_cmd,
+                                  invoke_diff3_cmd,
+                                  merge_options,
+                                  prop_diff,
+                                  cancel_func, cancel_baton,
+                                  scratch_pool, scratch_pool));
 
   /* If this isn't a dry run, then update the DB, run the work, and
    * call the conflict resolver callback.  */

Modified: subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/wc.h
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/wc.h?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/wc.h (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/libsvn_wc/wc.h Sat Nov  9 13:18:14 2013
@@ -440,6 +440,31 @@ svn_wc__internal_file_modified_p(svn_boo
    will be performed in SCRATCH_POOL.
 */
 svn_error_t *
+svn_wc__internal_merge1(svn_skel_t **work_items,
+                        svn_skel_t **conflict_skel,
+                        enum svn_wc_merge_outcome_t *merge_outcome,
+                        svn_wc__db_t *db,
+                        const char *left_abspath,
+                        const char *right_abspath,
+                        const char *target_abspath,
+                        const char *wri_abspath,
+                        const char *left_label,
+                        const char *right_label,
+                        const char *target_label,
+                        apr_hash_t *old_actual_props,
+                        svn_boolean_t dry_run,
+                        const char *invoke_diff3_cmd,
+                        const char *diff3_cmd,
+                        const apr_array_header_t *merge_options,
+                        const apr_array_header_t *prop_diff,
+                        svn_cancel_func_t cancel_func,
+                        void *cancel_baton,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool);
+
+/* As  svn_wc__internal_merge but without the invoke_diff3_cmd parameter. */
+SVN_DEPRECATED
+svn_error_t *
 svn_wc__internal_merge(svn_skel_t **work_items,
                        svn_skel_t **conflict_skel,
                        enum svn_wc_merge_outcome_t *merge_outcome,
@@ -461,6 +486,7 @@ svn_wc__internal_merge(svn_skel_t **work
                        apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool);
 
+
 /* A default error handler for svn_wc_walk_entries3().  Returns ERR in
    all cases. */
 svn_error_t *

Modified: subversion/branches/invoke-diff-merge-feature/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/svn/cl.h?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/svn/cl.h (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/svn/cl.h Sat Nov  9 13:18:14 2013
@@ -83,7 +83,11 @@ typedef enum svn_cl__accept_t
   svn_cl__accept_edit,
 
   /* Launch user's resolver and resolve conflict with edited file. */
-  svn_cl__accept_launch
+  svn_cl__accept_launch,
+
+  /* Launch user's resolver with the invoke_diff3_cmd in the config
+     file and resolve conflict with edited file. */
+  svn_cl__accept_invoke_diff3_config
 
 } svn_cl__accept_t;
 
@@ -97,6 +101,7 @@ typedef enum svn_cl__accept_t
 #define SVN_CL__ACCEPT_THEIRS_FULL "theirs-full"
 #define SVN_CL__ACCEPT_EDIT "edit"
 #define SVN_CL__ACCEPT_LAUNCH "launch"
+#define SVN_CL__ACCEPT_INVOKE_DIFF3_CONFIG "invoke-diff3-config"
 
 /* Return the svn_cl__accept_t value corresponding to WORD, using exact
  * case-sensitive string comparison. Return svn_cl__accept_invalid if WORD
@@ -204,6 +209,9 @@ typedef struct svn_cl__opt_state_t
   svn_boolean_t revprop;         /* operate on a revision property */
   const char *merge_cmd;         /* the external merge command to use
                                     (not converted to UTF-8) */
+  const char *invoke_diff3_cmd;  /* the format string for the external
+                                    merge command to use (not
+                                    converted to UTF-8) */
   const char *editor_cmd;        /* the external editor command to use
                                     (not converted to UTF-8) */
   svn_boolean_t record_only;     /* whether to record mergeinfo */
@@ -531,6 +539,19 @@ svn_cl__merge_file_externally(const char
                               svn_boolean_t *remains_in_conflict,
                               apr_pool_t *pool);
 
+/* As svn_cl__merge_file_externally, but for the invoke_diff3_cmd
+   selected merge tool  */
+svn_error_t *
+svn_cl__invoke_diff3_cmd_file_externally(const char *base_path,
+                                         const char *their_path,
+                                         const char *my_path,
+                                         const char *merged_path,
+                                         const char *wc_path,
+                                         apr_hash_t *config,
+                                         svn_boolean_t *remains_in_conflict,
+                                         apr_pool_t *pool);
+
+
 /* Like svn_cl__merge_file_externally, but using a built-in merge tool
  * with help from an external editor specified by EDITOR_CMD. */
 svn_error_t *

Modified: subversion/branches/invoke-diff-merge-feature/subversion/svn/conflict-callbacks.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/svn/conflict-callbacks.c?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/svn/conflict-callbacks.c (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/svn/conflict-callbacks.c Sat Nov  9 13:18:14 2013
@@ -458,6 +458,18 @@ static const resolver_option_t text_conf
                                      -1 },
   { "l",  N_("launch tool"),      N_("launch external tool to resolve "
                                      "conflict  [launch]"), -1 },
+  { "3f", N_("invoke-diff3-cmd given in config file"), 
+                                  N_("use invoke-diff3 command defined in "
+                                     "the config file to resolve conflict "
+                                     "[invoke-diff3-config]"), -1 },
+  { "3c", N_("invoke-diff3-cmd given on command line"),      
+                                  N_("use invoke-diff3 tool defined in the "
+                                     "commandline to resolve conflict "
+                                     "[invoke-diff3-cmd]"), -1 },
+  { "3i", N_("interactive invoke-diff3-cmd selection"), 
+                                  N_("interactively select tool now to "
+                                     "resolve conflict"), -1 },
+
   { "p",  N_("postpone"),         N_("mark the conflict to be resolved later"
                                      "  [postpone]"),
                                   svn_wc_conflict_choose_postpone },
@@ -735,6 +747,10 @@ handle_text_conflict(svn_wc_conflict_res
           *next_option++ = "e";
           *next_option++ = "m";
 
+          *next_option++ = "3f";
+          *next_option++ = "3c";
+          *next_option++ = "3i";
+
           if (knows_something)
             *next_option++ = "r";
 
@@ -813,7 +829,9 @@ handle_text_conflict(svn_wc_conflict_res
             knows_something = TRUE;
         }
       else if (strcmp(opt->code, "m") == 0 || strcmp(opt->code, ":-g") == 0 ||
-               strcmp(opt->code, "=>-") == 0 || strcmp(opt->code, ":>.") == 0)
+               strcmp(opt->code, "=>-") == 0 || strcmp(opt->code, ":>.") == 0 ||
+               strcmp(opt->code, "3f") == 0 || strcmp(opt->code, "3c") == 0 ||
+               strcmp(opt->code, "3i") == 0)
         {
           svn_boolean_t remains_in_conflict;
           svn_error_t *err;
@@ -1294,6 +1312,56 @@ conflict_func_interactive(svn_wc_conflic
         }
       /* else, fall through to prompting. */
       break;
+    case svn_cl__accept_invoke_diff3_config:
+      if (desc->base_abspath && desc->their_abspath
+          && desc->my_abspath && desc->merged_file)
+        {
+          svn_boolean_t remains_in_conflict;
+
+          if (b->external_failed)
+            {
+              (*result)->choice = svn_wc_conflict_choose_postpone;
+              return SVN_NO_ERROR;
+            }
+
+          err = svn_cl__invoke_diff3_cmd_file_externally(desc->base_abspath,
+                                              desc->their_abspath,
+                                              desc->my_abspath,
+                                              desc->merged_file,
+                                              desc->local_abspath,
+                                              b->config,
+                                              &remains_in_conflict,
+                                              scratch_pool);
+          if (err && err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL)
+            {
+              SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
+                                          err->message ? err->message :
+                                          _("No invoke-diff3-cmd tool found;"
+                                            " leaving all conflicts.")));
+              b->external_failed = TRUE;
+              return svn_error_trace(err);
+            }
+          else if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)
+            {
+              SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
+                                          err->message ? err->message :
+                                          _("Error running invoke-diff2-cmd tool;"
+                                            " leaving all conflicts.")));
+              b->external_failed = TRUE;
+              return svn_error_trace(err);
+            }
+          else if (err)
+            return svn_error_trace(err);
+
+          if (remains_in_conflict)
+            (*result)->choice = svn_wc_conflict_choose_postpone;
+          else
+            (*result)->choice = svn_wc_conflict_choose_merged;
+          return SVN_NO_ERROR;
+        }
+      /* else, fall through to prompting. */
+      break;
+
     }
 
   /* Print a summary of conflicts before starting interactive resolution */

Modified: subversion/branches/invoke-diff-merge-feature/subversion/svn/svn.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/svn/svn.c?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/svn/svn.c (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/svn/svn.c Sat Nov  9 13:18:14 2013
@@ -95,6 +95,7 @@ typedef enum svn_cl__longopt_t {
   opt_ignore_externals,
   opt_incremental,
   opt_merge_cmd,
+  opt_diff3_cmd,
   opt_native_eol,
   opt_new_cmd,
   opt_no_auth_cache,
@@ -250,6 +251,24 @@ const apr_getopt_option_t svn_cl__option
   {"ignore-externals", opt_ignore_externals, 0,
                     N_("ignore externals definitions")},
   {"diff3-cmd",     opt_merge_cmd, 1, N_("use ARG as merge command")},
+  {"invoke-diff3-cmd", opt_invoke_diff_cmd, 1, 
+                   N_("use ARG as format string for external merge program\n"
+                      "                             "
+                      "invocation.  Substitutions: \n" 
+                      "                             "
+                      "  %svn_mine  'mine' file\n"
+                      "                             "
+                      "  %svn_yours 'yours' file\n"
+                      "                "
+                      "  %svn_base  'base' file\n"
+                      "                "
+                      "  %svn_label_mine   label of the 'mine file\n"
+                      "                "
+                      "  %svn_label_yours  label of the 'yours' file\n"
+                      "                "
+                      "  %svn_label_base   label of the 'mine file\n"
+                      "                             "
+                      "See 'help diff' for example usage.")},
   {"editor-cmd",    opt_editor_cmd, 1, N_("use ARG as external editor")},
   {"record-only",   opt_record_only, 0,
                     N_("merge only mergeinfo differences")},
@@ -545,7 +564,7 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "  for writing by another Subversion client.\n"
      "  Note that the 'svn status' command shows unversioned items as '?',\n"
      "  and ignored items as 'I' if the --no-ignore option is given to it.\n"),
-    {opt_merge_cmd, opt_remove_unversioned, opt_remove_ignored,
+    {opt_merge_cmd, opt_diff3_cmd, opt_remove_unversioned, opt_remove_ignored,
      opt_include_externals, 'q'} },
 
   { "commit", svn_cl__commit, {"ci"},
@@ -1139,8 +1158,8 @@ const svn_opt_subcommand_desc2_t svn_cl_
 "  target. Also, merge-tracking is not supported for merges from foreign\n"
 "  repositories.\n"),
     {'r', 'c', 'N', opt_depth, 'q', opt_force, opt_dry_run, opt_merge_cmd,
-     opt_record_only, 'x', opt_ignore_ancestry, opt_accept, opt_reintegrate,
-     opt_allow_mixed_revisions, 'v'} },
+     opt_diff3_cmd, opt_record_only, 'x', opt_ignore_ancestry, opt_accept, 
+     opt_reintegrate, opt_allow_mixed_revisions, 'v'} },
 
   { "mergeinfo", svn_cl__mergeinfo, {0}, N_
     ("Display merge-related information.\n"
@@ -1603,8 +1622,9 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "    svn switch --relocate http:// svn://\n"
      "    svn switch --relocate http://www.example.com/repo/project \\\n"
      "                          svn://svn.example.com/repo/project\n"),
-    { 'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_relocate,
-      opt_ignore_externals, opt_ignore_ancestry, opt_force, opt_accept},
+    { 'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_diff3_cmd,
+      opt_relocate, opt_ignore_externals, opt_ignore_ancestry, opt_force, 
+      opt_accept},
     {{opt_ignore_ancestry,
       N_("allow switching to a node with no common ancestor")}}
   },
@@ -1662,9 +1682,9 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "\n"
      "  Use the --set-depth option to set a new working copy depth on the\n"
      "  targets of this operation.\n"),
-    {'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_force,
-     opt_ignore_externals, opt_changelist, opt_editor_cmd, opt_accept,
-     opt_parents} },
+    {'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_diff3_cmd, 
+     opt_force, opt_ignore_externals, opt_changelist, opt_editor_cmd, 
+     opt_accept, opt_parents} },
 
   { "upgrade", svn_cl__upgrade, {0}, N_
     ("Upgrade the metadata storage format for a working copy.\n"
@@ -2173,6 +2193,9 @@ sub_main(int argc, const char *argv[], a
       case opt_merge_cmd:
         opt_state.merge_cmd = apr_pstrdup(pool, opt_arg);
         break;
+      case opt_diff3_cmd:
+        opt_state.invoke_diff3_cmd = apr_pstrdup(pool, opt_arg);
+        break;
       case opt_record_only:
         opt_state.record_only = TRUE;
         break;
@@ -2610,6 +2633,16 @@ sub_main(int argc, const char *argv[], a
       return EXIT_ERROR(err);
     }
 
+  /* Check for mutually exclusive args --diff3-cmd and
+     --invoke-diff3-cmd */
+  if (opt_state.merge_cmd && opt_state.invoke_diff3_cmd)
+    {
+      err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+                             _("--diff3-cmd  and --invoke-diff3-cmd are "
+                               "mutually exclusive"));
+      return EXIT_ERROR(err);
+    }
+
   /* Ensure that 'revision_ranges' has at least one item, and make
      'start_revision' and 'end_revision' match that item. */
   if (opt_state.revision_ranges->nelts == 0)
@@ -2829,8 +2862,15 @@ sub_main(int argc, const char *argv[], a
     svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
                    SVN_CONFIG_OPTION_INVOKE_DIFF_CMD, opt_state.diff.invoke_diff_cmd);
   if (opt_state.merge_cmd)
-    svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
-                   SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd);
+    {
+      svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
+                     SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd);
+    }
+  if (opt_state.invoke_diff3_cmd) 
+    {
+      svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
+                   SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD, opt_state.invoke_diff3_cmd);
+    }
   if (opt_state.diff.internal_diff)
     svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
                    SVN_CONFIG_OPTION_DIFF_CMD, NULL);
@@ -2954,6 +2994,12 @@ sub_main(int argc, const char *argv[], a
                                    _("--accept=%s incompatible with"
                                      " --non-interactive"),
                                    SVN_CL__ACCEPT_LAUNCH));
+      if (opt_state.accept_which == svn_cl__accept_invoke_diff3_config)
+        return EXIT_ERROR(
+                 svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                                   _("--accept=%s incompatible with"
+                                     " --non-interactive"),
+                                   SVN_CL__ACCEPT_LAUNCH));
 
       /* The default action when we're non-interactive is to postpone
        * conflict resolution. */

Modified: subversion/branches/invoke-diff-merge-feature/subversion/svn/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-merge-feature/subversion/svn/util.c?rev=1540299&r1=1540298&r2=1540299&view=diff
==============================================================================
--- subversion/branches/invoke-diff-merge-feature/subversion/svn/util.c (original)
+++ subversion/branches/invoke-diff-merge-feature/subversion/svn/util.c Sat Nov  9 13:18:14 2013
@@ -173,6 +173,104 @@ svn_cl__merge_file_externally(const char
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_cl__invoke_diff3_cmd_file_externally(const char *base_path,
+                                         const char *their_path,
+                                         const char *my_path,
+                                         const char *merged_path,
+                                         const char *wc_path,
+                                         apr_hash_t *config,
+                                         svn_boolean_t *remains_in_conflict,
+                                         apr_pool_t *pool)
+{
+  char *invoke_diff3_cmd;
+  /* Error if there is no editor specified */
+  /* not sure how or where to set this, but it loads on fail... so... FIXME? */
+  if (apr_env_get(&invoke_diff3_cmd, "SVN_INVOKE_DIFF3_CMD", pool) != APR_SUCCESS)
+    {
+      struct svn_config_t *cfg;
+      invoke_diff3_cmd = NULL;
+      cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
+      /* apr_env_get wants char **, this wants const char ** */
+      svn_config_get(cfg, (const char **)&invoke_diff3_cmd,
+                     SVN_CONFIG_SECTION_HELPERS,
+                     SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD, NULL);
+    }
+
+  if (invoke_diff3_cmd)
+    {
+      const char *c;
+
+      for (c = invoke_diff3_cmd; *c; c++)
+        if (!svn_ctype_isspace(*c))
+          break;
+
+      if (! *c)
+        return svn_error_create
+          (SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL,
+           _("The SVN_INVOKE_DIFF3_TOOL environment variable is empty or "
+             "consists solely of whitespace. Expected a shell command.\n"));
+    }
+  else
+      return svn_error_create
+        (SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL,
+         _("The environment variable SVN_INVOKE_DIFF3_TOOL and the invoke-diff3-cmd run-time "
+           "configuration option were not set.\n"));
+
+  {
+    const char ** cmd;
+
+    apr_pool_t *scratch_pool = svn_pool_create(pool); 
+
+    char *cwd;
+    int exitcode;
+
+    apr_status_t status = apr_filepath_get(&cwd, APR_FILEPATH_NATIVE, pool);
+    if (status != 0)
+      return svn_error_wrap_apr(status, NULL);
+
+    /* doppelt gemoppelt... FIXME */
+    if (0 == strlen(invoke_diff3_cmd)) 
+      return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, NULL);
+    
+    /* cmd = __create_custom_diff_cmd(mine_label, yours_label, older_label,  */
+    /*                                mine, yours, older,  */
+    /*                                invoke_diff3_cmd, scratch_pool); */
+    cmd = __create_custom_diff_cmd(my_path, their_path, base_path, 
+                                   my_path, merged_path, wc_path, 
+                                   invoke_diff3_cmd, scratch_pool);
+    
+    SVN_ERR(svn_io_run_cmd(svn_dirent_internal_style(cwd, pool), 
+                           cmd[0], cmd, &exitcode, NULL, TRUE,
+                           NULL, NULL, NULL, scratch_pool));
+    
+    
+    /* According to the diff3 docs, a '0' means the merge was clean, and
+       '1' means conflict markers were found.  Anything else is real
+       error. */
+    if ((exitcode != 0) && (exitcode != 1))
+      {
+        
+        int i;
+        const char *failed_command = "";
+        
+      for (i = 0; cmd[i]; ++i)
+        failed_command = apr_pstrcat(pool, failed_command, 
+                                     cmd[i], " ", (char*) NULL);
+      svn_pool_destroy(scratch_pool);
+      return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+                               _("'%s' was expanded to '%s' and returned %d"),
+                               invoke_diff3_cmd,
+                               failed_command,
+                               exitcode);
+      }
+    else if (remains_in_conflict)
+      *remains_in_conflict = exitcode == 1;
+    svn_pool_destroy(scratch_pool);
+  }
+  return SVN_NO_ERROR;
+}
+
 
 /* A svn_client_ctx_t's log_msg_baton3, for use with
    svn_cl__make_log_msg_baton(). */