[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] Implement (l)aunch an external merge tool

From: Augie Fackler <durin42_at_gmail.com>
Date: 2007-09-20 03:32:33 CEST

Greetings all,

I've implemented launching of an external merge tool in the svn
binary. Unfortunately, my inspection of the world indicates that
there is no standard interface for merge tools, so I ended up
borrowing Mercurial's hgmerge shell script and modifying it to work
with the interface I devised.

Right now, the patch assumes the external merge tool accepts 4
arguments in the following order:
base left right merged
where merged is the destination for the file the merge tool will save
out. This reflects our tempfile model really well, but isn't how some
merge tools work (some merge tools seem to want to put the merge in
either base or right, which offends me on some level). I can provide
a script that wraps Apple's FileMerge tool if anyone is interested -
I'm hoping to finish patching up hgmerge to be svn_merge_helper that
will automagically pick the right choice from several options.

Note that I've got a couple of nitpicks of my own against this patch:
1) I use getwd() - is there some APR/Subversion function I can use
for this? I couldn't find anything, and this seems to work just fine.
2) It seems that there should be more ways to set the tool than
$SVNMERGE - probably a config variable. What's the procedure for
adding one? I'd love to go ahead and add that too.

(In the process of doing this, I found a couple of bugs and UI
nitpicks in other places - patches will follow soon, school
permitting (exam tomorrow, this is me procrastinating studying))

Peace,
Augie Fackler

[[[
Implement launching an external merge tool for interactive conflict
handling.

* subversion/include/svn_error_codes.h
   (SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL): New. Error code that
indicates no merge
    tool could be found.

* subversion/svn/cl.h
   (svn_cl__merge_file_externally): New prototype.

* subversion/svn/util.c
   Include unistd.h.
   (svn_cl__merge_file_externally): New. Used to invoke the user's
external merge
    tool on the specified files.

* subversion/svn/conflict-callbacks.c
   Include stdio.h.
   (svn_cl__interactive_conflict_handler): Implement calling an
external merge
    tool using svn_cl__merge_file_externally.
]]]

Index: subversion/include/svn_error_codes.h
===================================================================
--- subversion/include/svn_error_codes.h (revision 26684)
+++ subversion/include/svn_error_codes.h (working copy)
@@ -1124,6 +1124,10 @@
    SVN_ERRDEF(SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE,
               SVN_ERR_CL_CATEGORY_START + 9,
               "A log message was given where none was necessary")
+
+ SVN_ERRDEF(SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL,
+ SVN_ERR_CL_CATEGORY_START + 10,
+ "No external merge tool available")

  SVN_ERROR_END

Index: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h (revision 26684)
+++ subversion/svn/cl.h (working copy)
@@ -407,8 +407,16 @@
                               apr_hash_t *config,
                               apr_pool_t *pool);

+/* Search for a merge tool command in environment variables,
+ and use it to perform the merge of the four given files.
+ Use POOL for all allocations. */
+svn_error_t *
+svn_cl__merge_file_externally(const char *base_path,
+ const char *repos_path,
+ const char *user_path,
+ const char *merged_path,
+ apr_pool_t *pool);

-
  
  /*** Notification functions to display results on the terminal. */

Index: subversion/svn/util.c
===================================================================
--- subversion/svn/util.c (revision 26684)
+++ subversion/svn/util.c (working copy)
@@ -27,6 +27,7 @@
  #include <string.h>
  #include <ctype.h>
  #include <assert.h>
+#include <unistd.h>

  #include <apr_errno.h>
  #include <apr_strings.h>
@@ -193,8 +194,49 @@
    return SVN_NO_ERROR;
  }

+svn_error_t *
+svn_cl__merge_file_externally(const char *base_path,
+ const char *repos_path,
+ const char *user_path,
+ const char *merged_path,
+ apr_pool_t *pool)
+{
+ const char *merge_tool;
+ merge_tool = getenv("SVNMERGE");
+ /* Error if there is no editor specified */
+ if (merge_tool)
+ {
+ const char *c;

+ for (c = merge_tool; *c; c++)
+ if (!svn_ctype_isspace(*c))
+ break;

+ if (! *c)
+ return svn_error_create
+ (SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL,
+ _("The SVNMERGE 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 SVNMERGE is not set.\n"));
+
+ /* command base left right destination */
+ const char *arguments[] = { merge_tool, base_path, repos_path,
+ user_path, merged_path, NULL};
+ int exitcode;
+ apr_exit_why_e exitwhy;
+ char *sys_path = getwd(NULL);
+ const char *path = svn_path_internal_style(sys_path, pool);
+ free(sys_path);
+ SVN_ERR(svn_io_run_cmd(path, merge_tool, arguments, &exitcode,
&exitwhy, TRUE,
+ NULL, NULL, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
  svn_error_t *
  svn_cl__edit_string_externally(svn_string_t **edited_contents /*
UTF-8! */,
                                 const char **tmpfile_left /* UTF-8! */,
Index: subversion/svn/conflict-callbacks.c
===================================================================
--- subversion/svn/conflict-callbacks.c (revision 26684)
+++ subversion/svn/conflict-callbacks.c (working copy)
@@ -26,6 +26,7 @@
  #define APR_WANT_STDIO
  #define APR_WANT_STRFUNC
  #include <apr_want.h>
+#include <stdio.h>

  #include "svn_cmdline.h"
  #include "svn_client.h"
@@ -253,12 +254,37 @@
              }
            if (strcmp(answer, "l") == 0)
              {
- if (desc->base_file && desc->repos_file && desc-
>user_file)
+ if (desc->base_file && desc->repos_file &&
+ desc->user_file && desc->merged_file)
                  {
- /* ### TODO: launch $SVNMERGE tool here with 3
fulltexts. */
- SVN_ERR(svn_cmdline_printf(
- subpool, _("Feature not yet
implemented.\n\n")));
- performed_edit = TRUE;
+ svn_error_t *merge_err;
+ merge_err = svn_cl__merge_file_externally(desc-
>base_file,
+ desc-
>repos_file,
+ desc-
>user_file,
+ desc-
>merged_file,
+ pool);
+ if (merge_err &&
+ merge_err->apr_err ==
SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL)
+ {
+ SVN_ERR(svn_cmdline_printf(subpool, merge_err-
>message ?
+ merge_err->message :
+ _("No merge tool
found.\n")));
+ svn_error_clear(merge_err);
+ SVN_ERR(svn_cmdline_fflush(stdout));
+ }
+ else if (merge_err &&
+ merge_err->apr_err ==
SVN_ERR_EXTERNAL_PROGRAM)
+ {
+ SVN_ERR(svn_cmdline_printf(subpool, merge_err-
>message ?
+ merge_err->message :
+ _("Error running merge
tool.\n")));
+ svn_error_clear(merge_err);
+ SVN_ERR(svn_cmdline_fflush(stdout));
+ }
+ else if (merge_err)
+ return merge_err;
+ else
+ performed_edit = TRUE;
                  }
                else
                  SVN_ERR(svn_cmdline_printf(subpool, _("Invalid
option.\n\n")));

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Thu Sep 20 03:32:46 2007

This is an archived mail posted to the Subversion Dev mailing list.