Git fork

mergetool: new config guiDefault supports auto-toggling gui by DISPLAY

When no merge.tool or diff.tool is configured or manually selected, the
selection of a default tool is sensitive to the DISPLAY variable; in a
GUI session a gui-specific tool will be proposed if found, and
otherwise a terminal-based one. This "GUI-optimizing" behavior is
important because a GUI can make a huge difference to a user's ability
to understand and correctly complete a non-trivial conflicting merge.

Some time ago the merge.guitool and diff.guitool config options were
introduced to enable users to configure both a GUI tool, and a non-GUI
tool (with fallback if no GUI tool configured), in the same environment.

Unfortunately, the --gui argument introduced to support the selection of
the guitool is still explicit. When using configured tools, there is no
equivalent of the no-tool-configured "propose a GUI tool if we are in a GUI
environment" behavior.

As proposed in <xmqqmtb8jsej.fsf@gitster.g>, introduce new configuration
options, difftool.guiDefault and mergetool.guiDefault, supporting a special
value "auto" which causes the corresponding tool or guitool to be selected
depending on the presence of a non-empty DISPLAY value. Also support "true"
to say "default to the guitool (unless --no-gui is passed on the
commandline)", and "false" as the previous default behavior when these new
configuration options are not specified.

Signed-off-by: Tao Klerks <tao@klerks.biz>
Acked-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Tao Klerks and committed by
Junio C Hamano
42943b95 ae73b2c8

+179 -14
+7
Documentation/config/difftool.txt
··· 34 34 35 35 difftool.prompt:: 36 36 Prompt before each invocation of the diff tool. 37 + 38 + difftool.guiDefault:: 39 + Set `true` to use the `diff.guitool` by default (equivalent to specifying 40 + the `--gui` argument), or `auto` to select `diff.guitool` or `diff.tool` 41 + depending on the presence of a `DISPLAY` environment variable value. The 42 + default is `false`, where the `--gui` argument must be provided 43 + explicitly for the `diff.guitool` to be used.
+7
Documentation/config/mergetool.txt
··· 85 85 86 86 mergetool.prompt:: 87 87 Prompt before each invocation of the merge resolution program. 88 + 89 + mergetool.guiDefault:: 90 + Set `true` to use the `merge.guitool` by default (equivalent to 91 + specifying the `--gui` argument), or `auto` to select `merge.guitool` 92 + or `merge.tool` depending on the presence of a `DISPLAY` environment 93 + variable value. The default is `false`, where the `--gui` argument 94 + must be provided explicitly for the `merge.guitool` to be used.
+6 -4
Documentation/git-difftool.txt
··· 97 97 --[no-]gui:: 98 98 When 'git-difftool' is invoked with the `-g` or `--gui` option 99 99 the default diff tool will be read from the configured 100 - `diff.guitool` variable instead of `diff.tool`. The `--no-gui` 101 - option can be used to override this setting. If `diff.guitool` 102 - is not set, we will fallback in the order of `merge.guitool`, 103 - `diff.tool`, `merge.tool` until a tool is found. 100 + `diff.guitool` variable instead of `diff.tool`. This may be 101 + selected automatically using the configuration variable 102 + `difftool.guiDefault`. The `--no-gui` option can be used to 103 + override these settings. If `diff.guitool` is not set, we will 104 + fallback in the order of `merge.guitool`, `diff.tool`, 105 + `merge.tool` until a tool is found. 104 106 105 107 --[no-]trust-exit-code:: 106 108 'git-difftool' invokes a diff tool individually on each file.
+5 -4
Documentation/git-mergetool.txt
··· 85 85 the default merge tool will be read from the configured 86 86 `merge.guitool` variable instead of `merge.tool`. If 87 87 `merge.guitool` is not set, we will fallback to the tool 88 - configured under `merge.tool`. 88 + configured under `merge.tool`. This may be autoselected using 89 + the configuration variable `mergetool.guiDefault`. 89 90 90 91 --no-gui:: 91 - This overrides a previous `-g` or `--gui` setting and reads the 92 - default merge tool will be read from the configured `merge.tool` 93 - variable. 92 + This overrides a previous `-g` or `--gui` setting or 93 + `mergetool.guiDefault` configuration and reads the default merge 94 + tool from the configured `merge.tool` variable. 94 95 95 96 -O<orderfile>:: 96 97 Process files in the order specified in the
+12 -4
builtin/difftool.c
··· 685 685 686 686 int cmd_difftool(int argc, const char **argv, const char *prefix) 687 687 { 688 - int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0, 688 + int use_gui_tool = -1, dir_diff = 0, prompt = -1, symlinks = 0, 689 689 tool_help = 0, no_index = 0; 690 690 static char *difftool_cmd = NULL, *extcmd = NULL; 691 691 struct option builtin_difftool_options[] = { ··· 735 735 } else if (dir_diff) 736 736 die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index"); 737 737 738 - die_for_incompatible_opt3(use_gui_tool, "--gui", 738 + die_for_incompatible_opt3(use_gui_tool == 1, "--gui", 739 739 !!difftool_cmd, "--tool", 740 740 !!extcmd, "--extcmd"); 741 741 742 - if (use_gui_tool) 742 + /* 743 + * Explicitly specified GUI option is forwarded to git-mergetool--lib.sh; 744 + * empty or unset means "use the difftool.guiDefault config or default to 745 + * false". 746 + */ 747 + if (use_gui_tool == 1) 743 748 setenv("GIT_MERGETOOL_GUI", "true", 1); 744 - else if (difftool_cmd) { 749 + else if (use_gui_tool == 0) 750 + setenv("GIT_MERGETOOL_GUI", "false", 1); 751 + 752 + if (difftool_cmd) { 745 753 if (*difftool_cmd) 746 754 setenv("GIT_DIFF_TOOL", difftool_cmd, 1); 747 755 else
+5
git-difftool--helper.sh
··· 75 75 merge_tool="$GIT_DIFF_TOOL" 76 76 else 77 77 merge_tool="$(get_merge_tool)" 78 + subshell_exit_status=$? 79 + if test $subshell_exit_status -gt 1 80 + then 81 + exit $subshell_exit_status 82 + fi 78 83 fi 79 84 fi 80 85
+40
git-mergetool--lib.sh
··· 97 97 test "$TOOL_MODE" = merge 98 98 } 99 99 100 + get_gui_default () { 101 + if diff_mode 102 + then 103 + GUI_DEFAULT_KEY="difftool.guiDefault" 104 + else 105 + GUI_DEFAULT_KEY="mergetool.guiDefault" 106 + fi 107 + GUI_DEFAULT_CONFIG_LCASE=$(git config --default false --get "$GUI_DEFAULT_KEY" | tr 'A-Z' 'a-z') 108 + if test "$GUI_DEFAULT_CONFIG_LCASE" = "auto" 109 + then 110 + if test -n "$DISPLAY" 111 + then 112 + GUI_DEFAULT=true 113 + else 114 + GUI_DEFAULT=false 115 + fi 116 + else 117 + GUI_DEFAULT=$(git config --default false --bool --get "$GUI_DEFAULT_KEY") 118 + subshell_exit_status=$? 119 + if test $subshell_exit_status -ne 0 120 + then 121 + exit $subshell_exit_status 122 + fi 123 + fi 124 + echo $GUI_DEFAULT 125 + } 126 + 100 127 gui_mode () { 128 + if test -z "$GIT_MERGETOOL_GUI" 129 + then 130 + GIT_MERGETOOL_GUI=$(get_gui_default) 131 + if test $? -ne 0 132 + then 133 + exit 2 134 + fi 135 + fi 101 136 test "$GIT_MERGETOOL_GUI" = true 102 137 } 103 138 ··· 467 502 is_guessed=false 468 503 # Check if a merge tool has been configured 469 504 merge_tool=$(get_configured_merge_tool) 505 + subshell_exit_status=$? 506 + if test $subshell_exit_status -gt "1" 507 + then 508 + exit $subshell_exit_status 509 + fi 470 510 # Try to guess an appropriate merge tool if no tool has been set. 471 511 if test -z "$merge_tool" 472 512 then
+7 -2
git-mergetool.sh
··· 451 451 452 452 main () { 453 453 prompt=$(git config --bool mergetool.prompt) 454 - GIT_MERGETOOL_GUI=false 454 + GIT_MERGETOOL_GUI= 455 455 guessed_merge_tool=false 456 456 orderfile= 457 457 ··· 511 511 512 512 if test -z "$merge_tool" 513 513 then 514 - if ! merge_tool=$(get_merge_tool) 514 + merge_tool=$(get_merge_tool) 515 + subshell_exit_status=$? 516 + if test $subshell_exit_status = 1 515 517 then 516 518 guessed_merge_tool=true 519 + elif test $subshell_exit_status -gt 1 520 + then 521 + exit $subshell_exit_status 517 522 fi 518 523 fi 519 524 merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
+38
t/t7610-mergetool.sh
··· 860 860 git commit -m "test resolved with mergetool" 861 861 ' 862 862 863 + test_expect_success 'mergetool with guiDefault' ' 864 + test_config merge.guitool myguitool && 865 + test_config mergetool.myguitool.cmd "(printf \"gui \" && cat \"\$REMOTE\") >\"\$MERGED\"" && 866 + test_config mergetool.myguitool.trustExitCode true && 867 + test_when_finished "git reset --hard" && 868 + git checkout -b test$test_count branch1 && 869 + git submodule update -N && 870 + test_must_fail git merge main && 871 + 872 + test_config mergetool.guiDefault auto && 873 + DISPLAY=SOMETHING && export DISPLAY && 874 + yes "" | git mergetool both && 875 + yes "" | git mergetool file1 file1 && 876 + 877 + DISPLAY= && export DISPLAY && 878 + yes "" | git mergetool file2 "spaced name" && 879 + 880 + test_config mergetool.guiDefault true && 881 + yes "" | git mergetool subdir/file3 && 882 + 883 + yes "d" | git mergetool file11 && 884 + yes "d" | git mergetool file12 && 885 + yes "l" | git mergetool submod && 886 + 887 + echo "gui main updated" >expect && 888 + test_cmp expect file1 && 889 + 890 + echo "main new" >expect && 891 + test_cmp expect file2 && 892 + 893 + echo "gui main new sub" >expect && 894 + test_cmp expect subdir/file3 && 895 + 896 + echo "branch1 submodule" >expect && 897 + test_cmp expect submod/bar && 898 + git commit -m "branch1 resolved with mergetool" 899 + ' 900 + 863 901 test_done
+52
t/t7800-difftool.sh
··· 155 155 test_cmp expect actual 156 156 ' 157 157 158 + test_expect_success 'difftool with guiDefault auto selects gui tool when there is DISPLAY' ' 159 + difftool_test_setup && 160 + test_config merge.tool bogus-tool && 161 + test_config diff.tool bogus-tool && 162 + test_config diff.guitool test-tool && 163 + test_config difftool.guiDefault auto && 164 + DISPLAY=SOMETHING && export DISPLAY && 165 + 166 + echo branch >expect && 167 + git difftool --no-prompt branch >actual && 168 + test_cmp expect actual 169 + ' 170 + test_expect_success 'difftool with guiDefault auto selects regular tool when no DISPLAY' ' 171 + difftool_test_setup && 172 + test_config diff.guitool bogus-tool && 173 + test_config diff.tool test-tool && 174 + test_config difftool.guiDefault Auto && 175 + DISPLAY= && export DISPLAY && 176 + 177 + echo branch >expect && 178 + git difftool --no-prompt branch >actual && 179 + test_cmp expect actual 180 + ' 181 + 182 + test_expect_success 'difftool with guiDefault true selects gui tool' ' 183 + difftool_test_setup && 184 + test_config diff.tool bogus-tool && 185 + test_config diff.guitool test-tool && 186 + test_config difftool.guiDefault true && 187 + 188 + DISPLAY= && export DISPLAY && 189 + echo branch >expect && 190 + git difftool --no-prompt branch >actual && 191 + test_cmp expect actual && 192 + 193 + DISPLAY=Something && export DISPLAY && 194 + echo branch >expect && 195 + git difftool --no-prompt branch >actual && 196 + test_cmp expect actual 197 + ' 198 + 199 + test_expect_success 'difftool --no-gui trumps config guiDefault' ' 200 + difftool_test_setup && 201 + test_config diff.guitool bogus-tool && 202 + test_config diff.tool test-tool && 203 + test_config difftool.guiDefault true && 204 + 205 + echo branch >expect && 206 + git difftool --no-prompt --no-gui branch >actual && 207 + test_cmp expect actual 208 + ' 209 + 158 210 test_expect_success 'difftool --gui last setting wins' ' 159 211 difftool_test_setup && 160 212 : >expect &&