From patchwork Wed Jan 13 05:59:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicholas Guriev X-Patchwork-Id: 12015815 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A242EC433E0 for ; Wed, 13 Jan 2021 06:03:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 29BCF23120 for ; Wed, 13 Jan 2021 06:03:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725831AbhAMGD3 (ORCPT ); Wed, 13 Jan 2021 01:03:29 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41114 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725775AbhAMGDX (ORCPT ); Wed, 13 Jan 2021 01:03:23 -0500 X-Greylist: delayed 158 seconds by postgrey-1.37 at lindbergh.monkeyblade.net; Tue, 12 Jan 2021 22:02:37 PST Received: from dandelion.mymedia.su (unknown [IPv6:2604:180:2:1574::c9e3]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1F1ACC061575 for ; Tue, 12 Jan 2021 22:02:36 -0800 (PST) Received: from dandelion.mymedia.su (localhost.localdomain [127.0.0.1]) by dandelion.mymedia.su (8.15.2/8.15.2/Debian-3) with ESMTP id 10D5xwLS024227 for ; Wed, 13 Jan 2021 08:59:58 +0300 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=guriev.su; s=default; t=1610517598; bh=ok6D7N2wN6PZcSBtCAsHqSRpX+2Hoed/GGkkytcdO+Y=; h=Subject:From:To:Date:From; b=TIDGn+UKHwG0COIz1+PRYz7tukw4vkx6VTgnVa1F8Zx77Au5N9HC4hXwTzjBOs0Fz XcX2T2mGvwzWCGM1Xl3kVNvNEtjhC7zlznjInhB+QVPzEMI03Ib7ck/MgZ2Jh2Dq4P DCfcfQ1xv3wdymO8ps/oGQP190n5t8OAMUdjcXJI= Received: (from mymedia@localhost) by dandelion.mymedia.su (8.15.2/8.15.2/Submit) id 10D5xvr5024226 for git@vger.kernel.org; Wed, 13 Jan 2021 08:59:57 +0300 Message-ID: <2fb58fd30ae730ccd3e88ec51b5fe6d80ab7a8c7.camel@guriev.su> Subject: [RFC PATCH] mergetools: support difftool.tabbed setting From: Nicholas Guriev To: git@vger.kernel.org Date: Wed, 13 Jan 2021 08:59:55 +0300 User-Agent: Evolution 3.38.1-1 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org I was asked how to configure "git difftool" to open files using several tabs and stop spawning diff application on every modified file. I looked into Git source and found no possibility to run diff tool at one step. The patch allows a user to view diffs in single window at one go. The current implementation is still poor and it can be used solely for demonstration purposes. To see it in action, tweak the local gitconfig: git config difftool.prompt false git config difftool.tabbed true Then run: git difftool -t vimdiff Or: git difftool -t meld The solution has some restrictions, diffing up to ten files works now (I did not bother with dynamic memory allocation), and it does not handle spaces in file names (I do not know how to pass them correctly to underlying tools without "xargs -0"). I think the git-difftool--helper should be changed so that it could process many files in single invocation and it would not use a temporary file by itself. A similar behaviour can be done in git-mergetool, too. Do you have ideas how to better implement such a feature? Any comments are welcome. P.S.: I'm attaching screenshots for a clear demo what I mean. --- diff.c | 4 ++-- git-mergetool--lib.sh | 36 +++++++++++++++++++++++++++++++++++- mergetools/meld | 4 ++++ mergetools/vimdiff | 17 +++++++++++++++++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/diff.c b/diff.c index 2253ec880..8a265e0b0 100644 --- a/diff.c +++ b/diff.c @@ -532,7 +532,7 @@ static struct diff_tempfile { * this tempfile object is used to manage its lifetime. */ struct tempfile *tempfile; -} diff_temp[2]; +} diff_temp[20]; struct emit_callback { int color_diff; @@ -4275,7 +4275,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(); + //remove_tempfile(); strvec_clear(&argv); strvec_clear(&env); } diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index 7225abd81..e599e4243 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -193,6 +193,8 @@ setup_tool () { false } + unset -f diff_combo_cmd + if test -f "$MERGE_TOOLS_DIR/$tool" then . "$MERGE_TOOLS_DIR/$tool" @@ -248,6 +250,25 @@ trust_exit_code () { fi } +is_difftool_tabbed () { + : "${GIT_DIFFTOOL_TABBED:=$(git config --type=bool --default=false difftool.tabbed)}" + 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" -gt 1 \ + && type diff_combo_cmd >/dev/null 2>&1 +} + # Entry point for running tools run_merge_tool () { @@ -272,7 +293,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 " "$LOCAL" "$REMOTE" >> "$temp_file" + + if [ "$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 aab4ebb93..6570bf0f8 100644 --- a/mergetools/meld +++ b/mergetools/meld @@ -2,6 +2,10 @@ diff_cmd () { "$merge_tool_path" "$LOCAL" "$REMOTE" } +diff_combo_cmd () { + ( IFS=' '; "$merge_tool_path" $(printf ' --diff %s %s' `cat <&3`) 3<&- ) +} + merge_cmd () { check_meld_for_features diff --git a/mergetools/vimdiff b/mergetools/vimdiff index abc8ce4ec..31d6e1eaa 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 +' +diff_combo_cmd () { + ( IFS=' '; cd "$GIT_PREFIX" && "$merge_tool_path" -R -f \ + -c "$multitabbed_script" `cat <&3` 3<&- ) +} + merge_cmd () { case "$1" in *vimdiff) From patchwork Mon Jan 18 21:00:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicholas Guriev X-Patchwork-Id: 12028233 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CFCFBC433DB for ; Mon, 18 Jan 2021 21:04:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9E04D207C4 for ; Mon, 18 Jan 2021 21:04:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2394480AbhARVE1 (ORCPT ); Mon, 18 Jan 2021 16:04:27 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54292 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389954AbhARVD0 (ORCPT ); Mon, 18 Jan 2021 16:03:26 -0500 Received: from dandelion.mymedia.su (unknown [IPv6:2604:180:2:1574::c9e3]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A1A70C0613C1 for ; Mon, 18 Jan 2021 13:02:38 -0800 (PST) Received: from dandelion.mymedia.su (localhost.localdomain [127.0.0.1]) by dandelion.mymedia.su (8.15.2/8.15.2/Debian-3) with ESMTP id 10IL09Zb024950; Tue, 19 Jan 2021 00:00:10 +0300 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=guriev.su; s=default; t=1611003610; bh=/beYap/o+qTVLycEtD8N3sNqtaHPR1IZ9IOhLO749rM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=omWUaXDRnFI9GutRs/KApW1Brsb/A0LoMMPgRoU7WT+BbzqIAiG3tdiIuI03DdzFg UmOgfJRlvSchFhJue4bTCO5NLBj1/24S946ZyBe4Ye4IqMUPMVKLYXJ74hnv7TsK8c U7Ba/HEt+uv9CK746+JvYWNF4EqLuLMGv1dpcCiM= Received: (from mymedia@localhost) by dandelion.mymedia.su (8.15.2/8.15.2/Submit) id 10IL09bq024949; Tue, 19 Jan 2021 00:00:09 +0300 From: Nicholas Guriev To: git@vger.kernel.org Subject: [RFC PATCH v2 2/3] difftool-helper: conciliate difftool.tabbed and difftool.prompt settings Date: Tue, 19 Jan 2021 00:00:02 +0300 Message-Id: <20210118210003.3071205-3-nicholas@guriev.su> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210118210003.3071205-1-nicholas@guriev.su> References: <2fb58fd30ae730ccd3e88ec51b5fe6d80ab7a8c7.camel@guriev.su> <20210118210003.3071205-1-nicholas@guriev.su> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org The commit combines paths of compared files into a separate temporary file and prints them before launch the tool on last invocation of the helper. All temporary files will be removed before exiting after that. At least, we try. But it may happen the files remain in case of a bug or a random SIGKILL. --- git-difftool--helper.sh | 39 ++++++++++++++++++++++++++------------- git-mergetool--lib.sh | 34 +++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh index 46af3e60b7..529d55c96d 100755 --- a/git-difftool--helper.sh +++ b/git-difftool--helper.sh @@ -11,8 +11,8 @@ TOOL_MODE=diff # difftool.prompt controls the default prompt/no-prompt behavior # and is overridden with $GIT_DIFFTOOL*_PROMPT. should_prompt () { - prompt_merge=$(git config --bool mergetool.prompt || echo true) - prompt=$(git config --bool difftool.prompt || echo $prompt_merge) + prompt=$(git config --bool mergetool.prompt || echo true) + prompt=$(git config --bool difftool.prompt || echo $prompt) if test "$prompt" = true then test -z "$GIT_DIFFTOOL_NO_PROMPT" @@ -26,6 +26,18 @@ use_ext_cmd () { test -n "$GIT_DIFFTOOL_EXTCMD" } +prompt_before_launch () { + while true + do + printf "Launch '%s' [Y/n]? " "${GIT_DIFFTOOL_EXTCMD:-$merge_tool}" + read ans 2>/dev/null || return 1 + case "${ans-y}" in + [yY]*) return 0 ;; + [nN]*) return 1 ;; + esac + done +} + launch_merge_tool () { # Merged is the filename as it appears in the work tree # Local is the contents of a/filename @@ -40,19 +52,20 @@ launch_merge_tool () { # the user with the real $MERGED name before launching $merge_tool. if should_prompt then - printf "\nViewing (%s/%s): '%s'\n" "$GIT_DIFF_PATH_COUNTER" \ - "$GIT_DIFF_PATH_TOTAL" "$MERGED" - if use_ext_cmd + append_templist merged-paths "$MERGED" + # Do preinit before test whether the tool supports tabbed run. + use_ext_cmd || setup_tool "$merge_tool" + + if ! is_difftool_tabbed then - printf "Launch '%s' [Y/n]? " \ - "$GIT_DIFFTOOL_EXTCMD" - else - printf "Launch '%s' [Y/n]? " "$merge_tool" - fi - read ans || return - if test "$ans" = n + printf "\nViewing (%d/%d): '%s'\n" "$GIT_DIFF_PATH_COUNTER" \ + "$GIT_DIFF_PATH_TOTAL" "$MERGED" + prompt_before_launch || return + elif on_last_file then - return + printf "Viewing %d files:\n" "$GIT_DIFF_PATH_TOTAL" + printf " '%s'\n" `cat "$LAST_TEMPFILE"` + prompt_before_launch || return fi fi diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index 2a1edbb9b9..4bb0e31dac 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -22,6 +22,10 @@ is_available () { type "$merge_tool_path" >/dev/null 2>&1 } +on_last_file () { + test "$GIT_DIFF_PATH_COUNTER" -eq "$GIT_DIFF_PATH_TOTAL" 2>/dev/null +} + list_config_tools () { section=$1 line_prefix=${2:-} @@ -277,6 +281,26 @@ is_difftool_tabbed () { type diff_combo_cmd >/dev/null 2>&1 } +# Save lines to the chosen temporary file for usage from subsequent invocations. +# Path to the file will be placed to the LAST_TEMPFILE variable for later links. +append_templist () { + LAST_TEMPFILE="${TMPDIR:-/tmp}/git-${PPID}_$1" + shift + + if test "$GIT_DIFF_PATH_COUNTER" -eq 1 + then + printf '%s\n' "$@" >"$LAST_TEMPFILE" + else + printf '%s\n' "$@" >>"$LAST_TEMPFILE" + fi +} + +# Clean any temporary files that may create this script. +clean_templists () { + rm -f -- "${TMPDIR:-/tmp}/git-${PPID}"_* +} +trap 'on_last_file && clean_templists' EXIT INT TERM + # Entry point for running tools run_merge_tool () { @@ -303,14 +327,10 @@ run_merge_tool () { run_diff_cmd () { 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" + append_templist tabbed-queue "$LOCAL" "$REMOTE" + if on_last_file then - diff_combo_cmd 3<"$temp_file" - rm -f -- "$temp_file" + diff_combo_cmd 3<"$LAST_TEMPFILE" fi else diff_cmd "$1"