The patch introduces a new boolean setting, difftool.tabbed, which allows a user to view diffs in single window at one go. The `--tabbed` command line option and the GIT_DIFFTOOL_TABBED environment variable are also available. For now, it works only with vimdiff and related, meld. We call to a new invented function, diff_combo_cmd, if it is provided by the diff tool, and pass to it a list of pairs of compared files. The list is available through third file descriptor, filenames are separated by a line feed, '\n', so they can be split by means of unmodified $IFS. The function may close that descriptor after reading the list. --- builtin/difftool.c | 7 ++++++- diff.c | 10 +++++++++- git-mergetool--lib.sh | 42 +++++++++++++++++++++++++++++++++++++++++- mergetools/meld | 4 ++++ mergetools/vimdiff | 17 +++++++++++++++++ 5 files changed, 77 insertions(+), 3 deletions(-) diff --git a/builtin/difftool.c b/builtin/difftool.c index 6e18e623fd..f061d3c029 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -689,7 +689,7 @@ static int run_file_diff(int prompt, const char *prefix, int cmd_difftool(int argc, const char **argv, const char *prefix) { int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0, - tool_help = 0, no_index = 0; + tabbed = -1, tool_help = 0, no_index = 0; static char *difftool_cmd = NULL, *extcmd = NULL; struct option builtin_difftool_options[] = { OPT_BOOL('g', "gui", &use_gui_tool, @@ -708,6 +708,8 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "tool-help", &tool_help, N_("print a list of diff tools that may be used with " "`--tool`")), + OPT_BOOL_F(0, "tabbed", &tabbed, + N_("use tabs in diff tool if supported"), 0), OPT_BOOL(0, "trust-exit-code", &trust_exit_code, N_("make 'git-difftool' exit when an invoked diff " "tool returns a non - zero exit code")), @@ -756,6 +758,9 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) die(_("no <cmd> given for --extcmd=<cmd>")); } + if (tabbed >= 0) + setenv("GIT_DIFFTOOL_TABBED", tabbed ? "true" : "false", 1); + setenv("GIT_DIFFTOOL_TRUST_EXIT_CODE", trust_exit_code ? "true" : "false", 1); diff --git a/diff.c b/diff.c index 2253ec8802..2868848bba 100644 --- a/diff.c +++ b/diff.c @@ -1730,6 +1730,14 @@ static struct diff_tempfile *claim_diff_tempfile(void) BUG("diff is failing to clean up its tempfiles"); } +static void forget_tempfile(void) +{ + for (int i = 0; i < ARRAY_SIZE(diff_temp); i++) { + close_tempfile_gently(diff_temp[i].tempfile); + diff_temp[i] = (struct diff_tempfile){0}; + } +} + static void remove_tempfile(void) { int i; @@ -4275,7 +4283,7 @@ static void run_external_diff(const char *pgm, if (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v)) die(_("external diff died, stopping at %s"), name); - remove_tempfile(); + forget_tempfile(); strvec_clear(&argv); strvec_clear(&env); } diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index 78f3647ed9..2a1edbb9b9 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -195,6 +195,11 @@ setup_tool () { false } + # Clear combo function declared by a previous tool (if any) to + # unambiguously indicate that the current one supports the feature or + # not. + unset -f diff_combo_cmd + if test -f "$MERGE_TOOLS_DIR/$tool" then . "$MERGE_TOOLS_DIR/$tool" @@ -250,6 +255,28 @@ trust_exit_code () { fi } +# Check whether tabbed mode is requested and current loaded tool supports it. +is_difftool_tabbed () { + : "${GIT_DIFFTOOL_TABBED=$(git config --type=bool \ + --default=false difftool.tabbed || echo bad value)}" + case $(printf "%s" "$GIT_DIFFTOOL_TABBED" | tr '[:upper:]' '[:lower:]') in + yes|on|true|1) + GIT_DIFFTOOL_TABBED=true + ;; + no|off|false|0|'') + GIT_DIFFTOOL_TABBED=false + ;; + *) + echo "error: bad boolean value of GIT_DIFFTOOL_TABBED" >&2 + exit 1 + ;; + esac + + test "$GIT_DIFFTOOL_TABBED" = true && + test "${GIT_DIFF_PATH_TOTAL=0}" -gt 1 && + type diff_combo_cmd >/dev/null 2>&1 +} + # Entry point for running tools run_merge_tool () { @@ -274,7 +301,20 @@ run_merge_tool () { # Run a either a configured or built-in diff tool run_diff_cmd () { - diff_cmd "$1" + if is_difftool_tabbed + then + temp_file="${TMPDIR:-/tmp}/git-${PPID}_tabbed-queue" + test "$GIT_DIFF_PATH_COUNTER" -eq 1 && >"$temp_file" + printf '%s\n' "$LOCAL" "$REMOTE" >>"$temp_file" + + if test "$GIT_DIFF_PATH_COUNTER" -eq "$GIT_DIFF_PATH_TOTAL" + then + diff_combo_cmd 3<"$temp_file" + rm -f -- "$temp_file" + fi + else + diff_cmd "$1" + fi } # Run a either a configured or built-in merge tool diff --git a/mergetools/meld b/mergetools/meld index aab4ebb935..2f40262a70 100644 --- a/mergetools/meld +++ b/mergetools/meld @@ -2,6 +2,10 @@ diff_cmd () { "$merge_tool_path" "$LOCAL" "$REMOTE" } +diff_combo_cmd () { + "$merge_tool_path" $(printf -- '--diff\n%s\n%s\n' `cat <&3`) 3<&- +} + merge_cmd () { check_meld_for_features diff --git a/mergetools/vimdiff b/mergetools/vimdiff index abc8ce4ec4..006e7b825d 100644 --- a/mergetools/vimdiff +++ b/mergetools/vimdiff @@ -3,6 +3,23 @@ diff_cmd () { -c 'wincmd l' -c 'cd $GIT_PREFIX' "$LOCAL" "$REMOTE" } +multitabbed_script=' + let i = 1 + while i < argc() + execute "tabedit" fnameescape(argv(i - 1)) + execute "diffsplit" fnameescape(argv(i)) + wincmd L + let i = i + 2 + endwhile + tabfirst + tabclose + unlet i + chdir $GIT_PREFIX +' +diff_combo_cmd () { + "$merge_tool_path" -R -f -c "$multitabbed_script" -- `cat <&3` 3<&- +} + merge_cmd () { case "$1" in *vimdiff) -- 2.27.0