git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC PATCH] mergetools: support difftool.tabbed setting

From
NGNicholas Guriev <nicholas@guriev.su>
Date
Jan 13, 2021, 05:59 UTC
Message-ID
<2fb58fd30ae730ccd3e88ec51b5fe6d80ab7a8c7.camel@guriev.su>

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)
Next: Nicholas Guriev
Message 1 of 14 in “mergetools: support difftool.tabbed setting”
  1. mergetools: support difftool.tabbed settingNicholas Guriev, Jan 13, 2021
  2. 0/3 implement tabbed mode in difftoolsNicholas Guriev, Jan 18, 2021
  3. 2/3 difftool-helper: conciliate difftool.tabbed and difftool.prompt settingsNicholas Guriev, Jan 18, 2021
  4. 1/3 mergetools: support difftool.tabbed settingNicholas Guriev, Jan 18, 2021
  5. Junio C HamanoJan 18, 2021
  6. 3/3 doc: describe new difftool.tabbed featureNicholas Guriev, Jan 18, 2021
  7. 0/4 difftools in tabbed modeNicholas Guriev, Jan 25, 2021
  8. 4/4 t7800: new tests of difftool.tabbed featureNicholas Guriev, Jan 25, 2021
  9. 3/4 doc: describe new difftool.tabbed featureNicholas Guriev, Jan 25, 2021
  10. 1/4 mergetools: support difftool.tabbed settingNicholas Guriev, Jan 25, 2021
  11. 2/4 difftool-helper: conciliate difftool.tabbed and difftool.prompt settingsNicholas Guriev, Jan 25, 2021
  12. Junio C HamanoJan 25, 2021
  13. David AguilarFeb 12, 2021
  14. Junio C HamanoFeb 12, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.