{"thread":{"id":"12143","subject":"[PATCH 1/4] Teach git mergetool to use custom commands defined at config time","startedAt":"2008-02-17T10:23:08Z","lastAt":"2008-02-17T11:08:39Z","messageCount":5,"participants":["Charles Bailey","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"68968","messageId":"b63a66ef2a97cd3e791476a74bdb7081bcd57637.1203242325.git.charles@hashpling.org","threadId":"12143","inReplyTo":null,"subject":"[PATCH 1/4] Teach git mergetool to use custom commands defined at config time","fromName":"Charles Bailey","fromEmail":"charles@hashpling.org","sentAt":"2008-02-17T10:23:08Z","receivedAt":"2008-02-17T10:23:08Z","isPatch":true,"sender":{"key":"charles@hashpling.org","avatar":"https://avatars.githubusercontent.com/u/1668475?v=4"},"body":"Currently git mergetool is restricted to a set of commands defined\nin the script. You can subvert the mergetool.<tool>.path to force\ngit mergetool to use a different command, but if you have a command\nwhose invocation syntax does not match one of the current tools then\nyou would have to write a wrapper script for it.\n\nThis patch adds three git config variable patterns which allow a more\nflexible choice of merge tool.\n\nIf you run git mergetool with -t/--tool or the merge.tool config\nvariable set to an unrecognized tool then git mergetool will query the\nmergetool.<tool>.cmd config variable. If this variable exists, then\ngit mergetool will treat the specified tool as a custom command and\nwill use a shell eval to run the command so that the appropriate shell\nvariables can be used to find the merge temporary files.\n\nmergetool.<tool>.trustExitCode can be used to indicate that the exit\ncode of the custom command can be used to determine the success of the\nmerge. mergetool.<tool>.keepBackup can be used to specify whether the\noriginal pre-merge file with conflict markers should be kept as a\n'.orig' file after the merge tool completes.\n\nSigned-off-by: Charles Bailey <charles@hashpling.org>\n---\n\nThis series of patches are a cleaned and improved version of the\nearlier RFC patch that I sent.\n\nThey are in decreasing order of how much I care about / like them, and\nI'm happy to have them squashed or to submit a squashed set of 2,3 or 4\nof the patches.\n\n Documentation/config.txt |   25 +++++++++++++++++++++++--\n git-mergetool.sh         |   29 +++++++++++++++++++++++++++--\n 2 files changed, 50 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex f9bdb16..12d87d4 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -689,8 +689,10 @@ merge.summary::\n \n merge.tool::\n \tControls which merge resolution program is used by\n-\tlinkgit:git-mergetool[1].  Valid values are: \"kdiff3\", \"tkdiff\",\n-\t\"meld\", \"xxdiff\", \"emerge\", \"vimdiff\", \"gvimdiff\", and \"opendiff\".\n+\tlinkgit:git-mergetool[1].  Valid built-in values are: \"kdiff3\",\n+\t\"tkdiff\", \"meld\", \"xxdiff\", \"emerge\", \"vimdiff\", \"gvimdiff\", and\n+\t\"opendiff\".  Any other value is treated is custom merge tool\n+\tand there must be a corresponing mergetool.<tool>.cmd option.\n \n merge.verbosity::\n \tControls the amount of output shown by the recursive merge\n@@ -717,6 +719,25 @@ mergetool.<tool>.path::\n \tOverride the path for the given tool.  This is useful in case\n \tyour tool is not in the PATH.\n \n+mergetool.<tool>.cmd::\n+\tSpecify the command to invoke the specified merge tool.  The\n+\tspecified command is evaluated in shell with the following\n+\tvariables available: 'BASE' is the name of a temporary file\n+\tcontaining the common base of the files to be merged, if available;\n+\t'LOCAL' is the name of a temporary file containing the contents of\n+\tthe file on the current branch; 'REMOTE' is the name of a temporary\n+\tfile containing the contents of the file from the branch being\n+\tmerged; 'path' contains the name of the file to which the merge\n+\ttool should write the results of a successful merge.\n+\n+mergetool.<tool>.trustExitCode::\n+\tFor a custom merge command, specify whether the exit code of\n+\tthe merge command can be used to determine whether the merge was\n+\tsuccessful.  If this is not set to true then the merge target file\n+\ttimestamp is checked and the merge assumed to have been successful\n+\tif the file has been updated, otherwise the user is prompted to\n+\tindicate the success of the merge.\n+\n pack.window::\n \tThe size of the window used by linkgit:git-pack-objects[1] when no\n \twindow size is given on the command line. Defaults to 10.\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex cbbb707..cf30e21 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -271,6 +271,19 @@ merge_file () {\n \t    status=$?\n \t    save_backup\n \t    ;;\n+\t*)\n+\t    if test -n \"$merge_tool_cmd\"; then\n+\t\tif test \"$merge_tool_trust_exit_code\" = \"false\"; then\n+\t\t    touch \"$BACKUP\"\n+\t\t    ( eval $merge_tool_cmd )\n+\t\t    check_unchanged\n+\t\telse\n+\t\t    ( eval $merge_tool_cmd )\n+\t\t    status=$?\n+\t\tfi\n+\t\tsave_backup\n+\t    fi\n+\t    ;;\n     esac\n     if test \"$status\" -ne 0; then\n \techo \"merge of $path failed\" 1>&2\n@@ -309,12 +322,20 @@ do\n     shift\n done\n \n+valid_custom_tool()\n+{\n+    merge_tool_cmd=\"$(git config mergetool.$1.cmd)\"\n+    test -n \"$merge_tool_cmd\"\n+}\n+\n valid_tool() {\n \tcase \"$1\" in\n \t\tkdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)\n \t\t\t;; # happy\n \t\t*)\n-\t\t\treturn 1\n+\t\t\tif ! valid_custom_tool \"$1\"; then\n+\t\t\t\treturn 1\n+\t\t\tfi\n \t\t\t;;\n \tesac\n }\n@@ -380,10 +401,14 @@ else\n \n     init_merge_tool_path \"$merge_tool\"\n \n-    if ! type \"$merge_tool_path\" > /dev/null 2>&1; then\n+    if test -z \"$merge_tool_cmd\" && ! type \"$merge_tool_path\" > /dev/null 2>&1; then\n         echo \"The merge tool $merge_tool is not available as '$merge_tool_path'\"\n         exit 1\n     fi\n+\n+    if ! test -z \"$merge_tool_cmd\"; then\n+        merge_tool_trust_exit_code=\"$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)\"\n+    fi\n fi\n \n \n-- \n1.5.4.1.34.g94bf\n"},{"id":"68969","messageId":"2bc96ddc3df496c5d078c646336c85a15dd8e892.1203242325.git.charles@hashpling.org","threadId":"12143","inReplyTo":"b63a66ef2a97cd3e791476a74bdb7081bcd57637.1203242325.git.charles@hashpling.org","subject":"[PATCH 2/4] Changed the documented variable for mergetool's target file","fromName":"Charles Bailey","fromEmail":"charles@hashpling.org","sentAt":"2008-02-17T10:23:37Z","receivedAt":"2008-02-17T10:23:37Z","isPatch":true,"sender":{"key":"charles@hashpling.org","avatar":"https://avatars.githubusercontent.com/u/1668475?v=4"},"body":"Previously it was $path, now it is $MERGED which is more consistent\nwith the other documented variables for merge tools and a little\nmore meaningful.\n\nSigned-off-by: Charles Bailey <charles@hashpling.org>\n---\n Documentation/config.txt |    2 +-\n git-mergetool.sh         |    4 ++--\n 2 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 12d87d4..53f790d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -727,7 +727,7 @@ mergetool.<tool>.cmd::\n \t'LOCAL' is the name of a temporary file containing the contents of\n \tthe file on the current branch; 'REMOTE' is the name of a temporary\n \tfile containing the contents of the file from the branch being\n-\tmerged; 'path' contains the name of the file to which the merge\n+\tmerged; 'MERGED' contains the name of the file to which the merge\n \ttool should write the results of a successful merge.\n \n mergetool.<tool>.trustExitCode::\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex cf30e21..4681e9a 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -275,10 +275,10 @@ merge_file () {\n \t    if test -n \"$merge_tool_cmd\"; then\n \t\tif test \"$merge_tool_trust_exit_code\" = \"false\"; then\n \t\t    touch \"$BACKUP\"\n-\t\t    ( eval $merge_tool_cmd )\n+\t\t    ( MERGED=\"$path\" eval $merge_tool_cmd )\n \t\t    check_unchanged\n \t\telse\n-\t\t    ( eval $merge_tool_cmd )\n+\t\t    ( MERGED=\"$path\" eval $merge_tool_cmd )\n \t\t    status=$?\n \t\tfi\n \t\tsave_backup\n-- \n1.5.4.1.34.g94bf\n"},{"id":"68970","messageId":"b3bfecd3d3f3cbf5a0bbad8d65e6a7cda0e19d16.1203242325.git.charles@hashpling.org","threadId":"12143","inReplyTo":"b63a66ef2a97cd3e791476a74bdb7081bcd57637.1203242325.git.charles@hashpling.org","subject":"[PATCH 3/4] Allow a different mergetool command to be used for 'no base' merges","fromName":"Charles Bailey","fromEmail":"charles@hashpling.org","sentAt":"2008-02-17T10:24:04Z","receivedAt":"2008-02-17T10:24:04Z","isPatch":true,"sender":{"key":"charles@hashpling.org","avatar":"https://avatars.githubusercontent.com/u/1668475?v=4"},"body":"This adds a mergetool.<tool>.cmdNoBase config variable pattern to\nallow a different command syntax to be specified for custom merge\ntools for add/add conflicts where there is no base.\n\nThe same effects could have been achieved before this patch by adding\nsome shell test syntax in the mergetool.<tool>.cmd config variable but\nthis allows for easier to setup and more readable configurations.\n\nSigned-off-by: Charles Bailey <charles@hashpling.org>\n---\n Documentation/config.txt |    6 ++++++\n git-mergetool.sh         |   10 ++++++++--\n 2 files changed, 14 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 53f790d..06a93e0 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -730,6 +730,12 @@ mergetool.<tool>.cmd::\n \tmerged; 'MERGED' contains the name of the file to which the merge\n \ttool should write the results of a successful merge.\n \n+mergetool.<tool>.cmdNoBase::\n+\tIf present, the value of this variable is used instead of\n+\tmergetool.<tool>.cmd for conflicts where there is no common base\n+\t(i.e. add/add conflicts). The interpretation of this variable is\n+\totherwise identical to mergetool.<tool>.cmd.\n+\n mergetool.<tool>.trustExitCode::\n \tFor a custom merge command, specify whether the exit code of\n \tthe merge command can be used to determine whether the merge was\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex 4681e9a..2437c37 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -273,12 +273,17 @@ merge_file () {\n \t    ;;\n \t*)\n \t    if test -n \"$merge_tool_cmd\"; then\n+\t\tif base_present || test -z \"$merge_tool_no_base_cmd\"; then\n+\t\t    tool=\"$merge_tool_cmd\"\n+\t\telse\n+\t\t    tool=\"$merge_tool_no_base_cmd\"\n+\t\tfi\n \t\tif test \"$merge_tool_trust_exit_code\" = \"false\"; then\n \t\t    touch \"$BACKUP\"\n-\t\t    ( MERGED=\"$path\" eval $merge_tool_cmd )\n+\t\t    ( MERGED=\"$path\" eval $tool )\n \t\t    check_unchanged\n \t\telse\n-\t\t    ( MERGED=\"$path\" eval $merge_tool_cmd )\n+\t\t    ( MERGED=\"$path\" eval $tool )\n \t\t    status=$?\n \t\tfi\n \t\tsave_backup\n@@ -408,6 +413,7 @@ else\n \n     if ! test -z \"$merge_tool_cmd\"; then\n         merge_tool_trust_exit_code=\"$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)\"\n+        merge_tool_no_base_cmd=\"$(git config mergetool.$merge_tool.cmdNoBase)\"\n     fi\n fi\n \n-- \n1.5.4.1.34.g94bf\n"},{"id":"68971","messageId":"695bf8507497f15cefb1ca93feb02d632de3f854.1203242325.git.charles@hashpling.org","threadId":"12143","inReplyTo":"b63a66ef2a97cd3e791476a74bdb7081bcd57637.1203242325.git.charles@hashpling.org","subject":"[PATCH 4/4] Tidy up git mergetool's handling of backup files","fromName":"Charles Bailey","fromEmail":"charles@hashpling.org","sentAt":"2008-02-17T10:24:33Z","receivedAt":"2008-02-17T10:24:33Z","isPatch":true,"sender":{"key":"charles@hashpling.org","avatar":"https://avatars.githubusercontent.com/u/1668475?v=4"},"body":"Currently a backup pre-merge file with conflict markers is sometimes\nkept with a .orig extenstion and sometimes removed depending on the\nparticular merge tool used.\n\nThis patch makes the handling consistent across all merge tools and\nconfigurable via a new mergetool.keepBackup config variable\n\nSigned-off-by: Charles Bailey <charles@hashpling.org>\n---\n Documentation/config.txt |    5 +++++\n git-mergetool.sh         |   31 ++++++++++---------------------\n 2 files changed, 15 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 06a93e0..d9f62f2 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -744,6 +744,11 @@ mergetool.<tool>.trustExitCode::\n \tif the file has been updated, otherwise the user is prompted to\n \tindicate the success of the merge.\n \n+mergetool.keepBackup::\n+\tAfter performing a merge, the original file with conflict markers\n+\tcan be saved as a file with a `.orig` extension.  If this variable\n+\tis set to `false` then this file is not preserved.\n+\n pack.window::\n \tThe size of the window used by linkgit:git-pack-objects[1] when no\n \twindow size is given on the command line. Defaults to 10.\ndiff --git a/git-mergetool.sh b/git-mergetool.sh\nindex 2437c37..4397f47 100755\n--- a/git-mergetool.sh\n+++ b/git-mergetool.sh\n@@ -127,18 +127,6 @@ check_unchanged () {\n     fi\n }\n \n-save_backup () {\n-    if test \"$status\" -eq 0; then\n-\tmv -- \"$BACKUP\" \"$path.orig\"\n-    fi\n-}\n-\n-remove_backup () {\n-    if test \"$status\" -eq 0; then\n-\trm \"$BACKUP\"\n-    fi\n-}\n-\n merge_file () {\n     path=\"$1\"\n \n@@ -201,7 +189,6 @@ merge_file () {\n \t\t    -o \"$path\" -- \"$LOCAL\" \"$REMOTE\" > /dev/null 2>&1)\n \t    fi\n \t    status=$?\n-\t    remove_backup\n \t    ;;\n \ttkdiff)\n \t    if base_present ; then\n@@ -210,19 +197,16 @@ merge_file () {\n \t\t\"$merge_tool_path\" -o \"$path\" -- \"$LOCAL\" \"$REMOTE\"\n \t    fi\n \t    status=$?\n-\t    save_backup\n \t    ;;\n \tmeld|vimdiff)\n \t    touch \"$BACKUP\"\n \t    \"$merge_tool_path\" -- \"$LOCAL\" \"$path\" \"$REMOTE\"\n \t    check_unchanged\n-\t    save_backup\n \t    ;;\n \tgvimdiff)\n \t\ttouch \"$BACKUP\"\n \t\t\"$merge_tool_path\" -f -- \"$LOCAL\" \"$path\" \"$REMOTE\"\n \t\tcheck_unchanged\n-\t\tsave_backup\n \t\t;;\n \txxdiff)\n \t    touch \"$BACKUP\"\n@@ -240,7 +224,6 @@ merge_file () {\n \t\t    --merged-file \"$path\" -- \"$LOCAL\" \"$REMOTE\"\n \t    fi\n \t    check_unchanged\n-\t    save_backup\n \t    ;;\n \topendiff)\n \t    touch \"$BACKUP\"\n@@ -250,7 +233,6 @@ merge_file () {\n \t\t\"$merge_tool_path\" \"$LOCAL\" \"$REMOTE\" -merge \"$path\" | cat\n \t    fi\n \t    check_unchanged\n-\t    save_backup\n \t    ;;\n \tecmerge)\n \t    touch \"$BACKUP\"\n@@ -260,7 +242,6 @@ merge_file () {\n \t\t\"$merge_tool_path\" \"$LOCAL\" \"$REMOTE\" --mode=merge2 --to=\"$path\"\n \t    fi\n \t    check_unchanged\n-\t    save_backup\n \t    ;;\n \temerge)\n \t    if base_present ; then\n@@ -269,7 +250,6 @@ merge_file () {\n \t\t\"$merge_tool_path\" -f emerge-files-command \"$LOCAL\" \"$REMOTE\" \"$(basename \"$path\")\"\n \t    fi\n \t    status=$?\n-\t    save_backup\n \t    ;;\n \t*)\n \t    if test -n \"$merge_tool_cmd\"; then\n@@ -286,15 +266,22 @@ merge_file () {\n \t\t    ( MERGED=\"$path\" eval $tool )\n \t\t    status=$?\n \t\tfi\n-\t\tsave_backup\n \t    fi\n \t    ;;\n     esac\n+\n     if test \"$status\" -ne 0; then\n \techo \"merge of $path failed\" 1>&2\n \tmv -- \"$BACKUP\" \"$path\"\n \texit 1\n     fi\n+\n+    if test \"$merge_keep_backup\" = \"true\"; then\n+\tmv -- \"$BACKUP\" \"$path.orig\"\n+    else\n+\trm -- \"$BACKUP\"\n+    fi\n+\n     git add -- \"$path\"\n     cleanup_temp_files\n }\n@@ -406,6 +393,8 @@ else\n \n     init_merge_tool_path \"$merge_tool\"\n \n+    merge_keep_backup=\"$(git config --bool merge.keepBackup || echo true)\"\n+\n     if test -z \"$merge_tool_cmd\" && ! type \"$merge_tool_path\" > /dev/null 2>&1; then\n         echo \"The merge tool $merge_tool is not available as '$merge_tool_path'\"\n         exit 1\n-- \n1.5.4.1.34.g94bf\n"},{"id":"68976","messageId":"7vmypz262w.fsf@gitster.siamese.dyndns.org","threadId":"12143","inReplyTo":"b63a66ef2a97cd3e791476a74bdb7081bcd57637.1203242325.git.charles@hashpling.org","subject":"Re: [PATCH 1/4] Teach git mergetool to use custom commands defined at config time","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-17T11:08:39Z","receivedAt":"2008-02-17T11:08:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Charles Bailey <charles@hashpling.org> writes:\n\n> This series of patches are a cleaned and improved version of the\n> earlier RFC patch that I sent.\n\nAbout the organization of the series, I think (note that what I\nthink does not count as much as what Ted thinks around this\narea) it would make much more sense to do 4 first (unless there\nis a reason why it behaves differently for existing backends),\nthen 2 (remove $path everywhere and use $MERGED consistently,\nnot just where you call out to the custom tool), and then\nfinally 1.  The general idea is to clean-up first and then add\nfeatures on solidified base.\n\nI do not personally see much need for 3, as the custom script\nshould be able to check the situation and adjust its behaviour\naccordingly, and that way you do not have to maintain two\nscripts.  I.e. if $BASE does not exist, there is no base, no?\n"}]}