git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH 0/3] vimdiff: new layout option + docs
@ 2021-11-04 16:09 Fernando Ramos
  2021-11-04 16:09 ` [PATCH 1/3] vimdiff: new implementation with layout support Fernando Ramos
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Fernando Ramos @ 2021-11-04 16:09 UTC (permalink / raw)
  To: git; +Cc: gitster, davvid, seth, levraiphilippeblain, rogi

A few weeks ago I presented this RFC [1] where I introduced a new variant of the
vimdiff merge tool ("vimdiff4") that creates three tabs (instead of just one)
that look like this:

    ------------------------------------------
    | <TAB #1> |  TAB #2  |  TAB #3  |       |
    ------------------------------------------
    |             |           |              |
    |   LOCAL     |   BASE    |   REMOTE     |
    |             |           |              |   <---- Same information
    ------------------------------------------         presented by the
    |                                        |         "standard" vimdiff
    |                MERGED                  |         merge tool
    |                                        |
    ------------------------------------------
    
    ------------------------------------------
    |  TAB #1  | <TAB #2> |  TAB #3  |       |
    ------------------------------------------
    |                   |                    |
    |                   |                    |
    |                   |                    |
    |     BASE          |    LOCAL           |   <---- Only differences
    |                   |                    |         between BASE and
    |                   |                    |         LOCAL are shown
    |                   |                    |
    ------------------------------------------
    
    ------------------------------------------
    |  TAB #1  |  TAB #2  | <TAB #3> |       |
    ------------------------------------------
    |                   |                    |
    |                   |                    |
    |                   |                    |
    |     BASE          |    REMOTE          |   <---- Only differences
    |                   |                    |         between BASE and
    |                   |                    |         REMOTE are shown
    |                   |                    |
    ------------------------------------------


The motivation behind this was that, for non-trivial merges, the three way diff
presented in the first tab tends to be very confusing and in these cases
indivial diffs between BASE and LOCAL and between BASE and REMOTE are very
useful.

I have been using a "custom" merge tool for months to achieve this same result
by adding these lines to my .gitconfig file:

  [mergetool "supermerge"]
        cmd = vim -f -d -c \"4wincmd w | wincmd J | tabnew | edit $LOCAL | vertical diffsplit $BASE | tabnew | edit $REMOTE | vertical diffsplit $BASE | 2tabprevious\" \"$LOCAL\" \"$BASE\" \"$REMOTE\" \"$MERGED\"
        trustExitCode = true

...and, because I found this "trick" very useful, I thought it would be a good
idea to add it as a git built-in merge tool (called "vimdiff4" because  1, 2 and
3 had already been taken) for everyone to use... and that's exactly what the RFC
I published did.

Now... as you can see in the RFC thread [1], David and Juno suggested that
maybe, instead of creating *yet another vimdiff variant*, we should take this
opportunity to:

  * Come up with a more general way of defining arbitrary vim layouts.
  
  * Re-implement "vimdiff1", "vimdiff2" and "vimdiff3" using this new mechanism
    (after all, the only difference among them is that they present different
    layouts to the user)

  * Add documentation to all of this.

And the result of that work is what I'm presenting today :)

Some things I would like to mention:

  1. There are three commits in this patch series:

     - The first one implements the logic to generate new arbitrary layouts and
       also re-defines "vimdiff1", "vimdiff2" and "vimdiff3" on top of it.

     - The second one adds documentation. It is probably a good idea to start
       reviewing this commit before the first one!

     - The last commit *is not meant to be merged now*. It removes "vimdiff1",
       "vimdiff2" and "vimdiff3", which is something that should only be done
       after one or two releases with a deprecation notice and only if everyone
       agrees to do so :)

  2. "mergetools/vimdiff" is now a ~800 lines bash script, but most of it is
     documentation (which is embedded in the tool itself for easier maintenance)
     and unit tests.
     I have only tested it with bash, but I've tried not to use any command not
     already being used somewhere else, so I expect it to work in the same
     places it was working before (however, let me know if there are some shell
     compatibility requirements and I'll try to check them).

  3. Regarding unit tests, "mergetool/vimdiff" contains instructions on how to
     run them (just call the script without arguments after making changes, to
     make sure you didn't break anything).
     Right now it prints "OK" on all test cases (obviously) [2]

  3. The "git {diff,merge}tool --tool-help" command now also prints the
     documentation for each tool (instead of just its name, as before).
     You can see an example of the output here ([3] and [4])

Finally, let me say that, while I like what this patch series achieves, I would
also *completely* understand if you decide not to merge it due to being a
complex solution to a simple problem that can be solved (as I had been doing up
until today) by just adding three line to one's .gitconfig.

  [mergetool "supermerge"]
        cmd = vim -f -d -c ...(custom complex sequence of vim commands)...
        trustExitCode = true

Let me know what you think.

Thanks.


References:

  [1] https://lore.kernel.org/git/20211019212020.25385-1-greenfoo@u92.eu/#r
  [2] https://pastebin.com/kuQ5pETG
  [3] https://pastebin.com/yvLWxeiM
  [4] https://pastebin.com/qNc7qymp


Fernando Ramos (3):
  vimdiff: new implementation with layout support
  vimdiff: add tool documentation
  vimdiff: remove deprecated {,g,n}vimdiff{1,2,3} variants

 git-mergetool--lib.sh |  12 +
 mergetools/vimdiff    | 697 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 677 insertions(+), 32 deletions(-)


base-commit: 876b1423317071f43c99666f3fc3db3642dfbe14
-- 
2.33.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/3] vimdiff: new implementation with layout support
  2021-11-04 16:09 [PATCH 0/3] vimdiff: new layout option + docs Fernando Ramos
@ 2021-11-04 16:09 ` Fernando Ramos
  2021-11-07 22:41   ` David Aguilar
  2021-11-04 16:09 ` [PATCH 2/3] vimdiff: add tool documentation Fernando Ramos
  2021-11-04 16:09 ` [PATCH 3/3] vimdiff: remove deprecated {,g,n}vimdiff{1,2,3} variants Fernando Ramos
  2 siblings, 1 reply; 9+ messages in thread
From: Fernando Ramos @ 2021-11-04 16:09 UTC (permalink / raw)
  To: git; +Cc: gitster, davvid, seth, levraiphilippeblain, rogi

When running 'git mergetool -t vimdiff', a new configuration option
('mergetool.vimdiff.layout') can now be used to select how the user
wants the different windows, tabs and buffers to be displayed.

If the option is not provided, the layout will be the same one that was
being used before this commit (ie. two rows with LOCAL, BASE and COMMIT
in the top one and MERGED in the bottom one).

The 'vimdiff' variants ('vimdiff{1,2,3}') still work but, because they
represented nothing else than different layouts, are now internally
implemented as a subcase of 'vimdiff' with the corresponding
pre-configured 'layout'.

Signed-off-by: Fernando Ramos <greenfoo@u92.eu>
---
 mergetools/vimdiff | 530 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 504 insertions(+), 26 deletions(-)

diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index 96f6209a04..1f2e88777e 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -1,48 +1,509 @@
+#!/bin/bash
+
+# This script can be run in two different contexts:
+#
+#   - From git, when the user invokes the "vimdiff" merge tool. In this context
+#     this script expects the following environment variables (among others) to
+#     be defined (which is something "git" takes care of):
+#
+#       - $BASE
+#       - $LOCAL
+#       - $REMOTE
+#       - $MERGED
+#
+#     In this mode, all this script does is to run the next command:
+#
+#         vim -f -c ... $LOCAL $BASE $REMOTE $MERGED
+#
+#     ...where the "..." string depends on the value of the
+#     "mergetool.vimdiff.layout" configuration variable and is used to open vim
+#     with a certain layout of buffers, windows and tabs.
+#
+#   - From the shell, manually. This is only expected to be done by developers
+#     who are editing this script. When this happens, the script runs a battery
+#     of unit tests to make sure nothing breaks.
+#     In this context this script does not expect any particular environment
+#     variable to be set.
+#
+
+
+# Set to "true" to print debug messages to stderr
+DEBUG=false
+#DEBUG=true
+
+
+################################################################################
+## Internal functions (not meant to be used outside this script)
+################################################################################
+
+debug_print() { 
+	# Send message to stderr if global variable DEBUG is set to "true"
+        
+	if test "$DEBUG" = "true"
+	then
+		>&2 echo "$@";
+	fi
+}
+
+
+gen_cmd_aux() {
+	# Auxiliary function used from "gen_cmd()".
+	# Read that other function documentation for more details.
+
+	local LAYOUT=$1 
+	local CMD=$2  # This is a second (hidden) argument used for recursion  
+
+	debug_print
+	debug_print "LAYOUT    : $LAYOUT"
+	debug_print "CMD       : $CMD"
+
+	if test -z "$CMD"
+	then
+		CMD="echo" # vim "nop" operator
+	fi
+
+	local start=0
+	local end=${#LAYOUT}
+
+	local nested=0
+	local nested_min=100
+
+
+	# Step 1:
+	#
+	# Increase/decrease "start"/"end" indices respectively to get rid of
+	# outer parenthesis.
+	#
+	# Example:
+	#
+	#   - BEFORE: (( LOCAL | BASE ) - MERGED )
+	#   - AFTER :  ( LOCAL | BASE ) - MERGED 
+
+	for (( i=$start; i<$end; i++ )); do
+		if test "${LAYOUT:$i:1}" = " "
+		then
+			continue
+		fi
+
+		if test "${LAYOUT:$i:1}" = "("
+		then
+			nested=$(( nested + 1 ))
+			continue
+		fi
+
+		if test "${LAYOUT:$i:1}" = ")"
+		then
+			nested=$(( nested - 1 ))
+			continue
+		fi
+
+		if test "$nested" -lt "$nested_min"
+		then
+			nested_min=$nested
+		fi
+	done
+
+	debug_print "NESTED MIN: $nested_min"
+
+	while test "$nested_min" -gt "0"
+	do
+		start=$(( start + 1 ))
+		end=$(( end - 1 ))
+
+		start_minus_one=$(( start - 1 ))
+
+		while ! test "${LAYOUT:$start_minus_one:1}" = "("
+		do
+			start=$(( start + 1 ))
+			start_minus_one=$(( start_minus_one + 1 ))
+		done
+
+		while ! test "${LAYOUT:$end:1}" = ")"
+		do
+			end=$(( end - 1 ))
+		done
+
+		nested_min=$(( nested_min - 1 ))
+	done
+
+        debug_print "CLEAN     : ${LAYOUT:$start:$(( end - start ))}"
+
+
+	# Step 2:
+	#
+	# Search for all valid separators (";", "-" or "|") which are *not*
+	# inside parenthesis. Save the index at which each of them makes the
+	# first appearance.
+
+	local index_semicolon=""
+	local index_minus=""
+	local index_pipe=""
+
+	nested=0
+	for (( i=$start; i<$end; i++ )); do
+		if test "${LAYOUT:$i:1}" = " "
+		then
+			continue
+		fi
+
+		if test "${LAYOUT:$i:1}" = "("
+		then
+			nested=$(( nested + 1 ))
+			continue
+		fi
+
+		if test "${LAYOUT:$i:1}" = ")"
+		then
+			nested=$(( nested - 1 ))
+			continue
+		fi
+
+		if test "$nested" -eq "0"
+		then
+			current=${LAYOUT:$i:1}
+			
+			if test "$current" = ";"
+			then
+				if test -z "$index_semicolon"
+				then
+					index_semicolon=$i
+				fi
+
+			elif test "$current" = "-"
+			then
+				if test -z "$index_minus"
+				then
+					index_minus=$i
+				fi
+
+			elif test "$current" = "|"
+			then
+				if test -z "$index_pipe"
+				then
+					index_pipe=$i
+				fi
+			fi
+		fi
+	done
+
+
+	# Step 3:
+	#
+	# Process the separator with the highest order of precedence
+	# (";" has the highest precedence and "|" the lowest one).
+	#
+	# By "process" I mean recursively call this function twice: the first
+	# one with the substring at the left of the separator and the second one
+	# with the one at its right.
+
+	local terminate="false"
+
+	if ! test -z "$index_semicolon"
+	then
+		before="-tabnew"
+		after="tabnext"
+		index=$index_semicolon
+		terminate="true"
+
+	elif ! test -z "$index_minus"
+	then
+		before="split"
+		after="wincmd j"
+		index=$index_minus
+		terminate="true"
+
+	elif ! test -z "$index_pipe"
+	then
+		before="vertical split"
+		after="wincmd l"
+		index=$index_pipe
+		terminate="true"
+	fi
+
+	if  test "$terminate" = "true"
+	then
+		CMD="$CMD | $before"
+		CMD=$(gen_cmd_aux "${LAYOUT:$start:$(( index - start ))}" "$CMD")
+		CMD="$CMD | $after"
+		CMD=$(gen_cmd_aux "${LAYOUT:$(( index + 1 )):$(( ${#LAYOUT} - index ))}" "$CMD")
+		echo $CMD
+		return
+	fi
+
+
+	# Step 4:
+	#
+	# If we reach this point, it means there are no separators and we just
+	# need to print the command to display the specified buffer
+
+	local target=$(echo "${LAYOUT:$start:$(( end - start ))}" | sed 's:[ *();|-]::g')
+
+	if test "$target" = "LOCAL"
+	then
+		CMD="$CMD | 1b"
+
+	elif test "$target" = "BASE"
+	then
+		CMD="$CMD | 2b"
+
+	elif test "$target" = "REMOTE"
+	then
+		CMD="$CMD | 3b"
+
+	elif test "$target" = "MERGED"
+	then
+		CMD="$CMD | 4b"
+
+	else
+		CMD="$CMD | ERROR: >$target<"
+	fi
+
+	echo $CMD
+	return
+}
+
+
+gen_cmd() {
+	# This function returns (in global variable FINAL_CMD) the string that
+	# you can use when invoking "vim" (as shown next) to obtain a given
+	# layout:
+	#
+	#   $ vim -f $FINAL_CMD "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
+	#
+	# It takes one single argument: a string containing the desired layout
+	# definition.
+	#
+	# The syntax of the "layout definitions" is explained in ... (TODO)...
+	# but you can already intuitively understand how it works by knowing
+	# that...
+	#
+	#   * ";" means "a new vim tab"
+	#   * "-" means "a new vim horizontal split"
+	#   * "|" means "a new vim vertical split"
+	#
+	# It also returns (in global variable FINAL_TARGET) the name ("LOCAL",
+	# "BASE", "REMOTE" or "MERGED") of the file that is marked with an "*",
+	# or "MERGED" if none of them is.
+	#
+	# Example:
+	#
+	#     gen_cmd "LOCAL* | REMOTE"
+	#     |
+	#     `-> FINAL_CMD    == "-c \"echo | vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\""
+	#         FINAL_TARGET == "LOCAL"
+
+	local LAYOUT=$1 
+
+
+	# Search for a "*" in one of the files identifiers ("LOCAL", "BASE",
+	# "REMOTE", "MERGED"). If not found, use "MERGE" as the default file
+	# where changes will be saved.
+
+	AUX=$(echo "$LAYOUT" | grep -oe "[A-Z]\+\*")
+
+	if ! test -z "$AUX"
+	then
+	        FINAL_TARGET="${AUX:0:-1}"
+	else
+	        FINAL_TARGET="MERGED"
+	fi
+
+
+	# Obtain the first part of vim "-c" option to obtain the desired layout
+
+	CMD=$(gen_cmd_aux "$LAYOUT")
+
+
+	# Adjust the just obtained script depending on whether more than one
+	# windows are visible or not
+
+	if test $(echo $LAYOUT | wc -w) == "1"
+	then
+		CMD="$CMD | bufdo diffthis"
+        else
+		CMD="$CMD | tabdo windo diffthis"
+	fi
+
+
+	# Add an extra "-c" option to move to the first tab (notice that we
+	# can't simply append the command to the previous "-c" string as
+	# explained here: https://github.com/vim/vim/issues/9076
+
+	FINAL_CMD="-c \"$CMD\" -c \"tabfirst\""
+}
+
+
+run_unit_tests() {
+	# Function to make sure that we don't break anything when modifying this
+	# script.
+	#
+	# This function is automatically executed when you execute this script
+	# from the shell with environment variable "DEBUG_GIT_VIMDIFF" set (to
+	# any value).
+
+	local test_cases=(
+		`#Test case 00` "LOCAL | MERGED | REMOTE"
+		`#Test case 01` "LOCAL - MERGED - REMOTE"
+		`#Test case 02` "(LOCAL - REMOTE) | MERGED"
+		`#Test case 03` "MERGED | (LOCAL - REMOTE)"
+		`#Test case 04` "(LOCAL | REMOTE) - MERGED"
+		`#Test case 05` "MERGED - (LOCAL | REMOTE)"
+		`#Test case 06` "(LOCAL | BASE | REMOTE) - MERGED"
+		`#Test case 07` "(LOCAL - BASE - REMOTE) | MERGED"
+		`#Test case 08` "LOCAL* | REMOTE"
+		`#Test case 09` "MERGED"
+		`#Test case 10` "(LOCAL | BASE | REMOTE) - MERGED; BASE | LOCAL; BASE | REMOTE; (LOCAL - BASE - REMOTE) | MERGED"
+		`#Test case 11` "((LOCAL | REMOTE) - BASE) | MERGED"
+		`#Test case 12` "((LOCAL | REMOTE) - BASE) | ((LOCAL - REMOTE) | MERGED)"
+		`#Test case 13` "BASE | REMOTE ; BASE | LOCAL"
+	)
+
+	local expected_cmd=(
+		`#Test case 00` "-c \"echo | vertical split | 1b | wincmd l | vertical split | 4b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 01` "-c \"echo | split | 1b | wincmd j | split | 4b | wincmd j | 3b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 02` "-c \"echo | vertical split | split | 1b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 03` "-c \"echo | vertical split | 4b | wincmd l | split | 1b | wincmd j | 3b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 04` "-c \"echo | split | vertical split | 1b | wincmd l | 3b | wincmd j | 4b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 05` "-c \"echo | split | 4b | wincmd j | vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 06` "-c \"echo | split | vertical split | 1b | wincmd l | vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 07` "-c \"echo | vertical split | split | 1b | wincmd j | split | 2b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 08` "-c \"echo | vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 09` "-c \"echo | 4b | bufdo diffthis\" -c \"tabfirst\""
+		`#Test case 10` "-c \"echo | -tabnew | split | vertical split | 1b | wincmd l | vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnext | -tabnew | vertical split | 2b | wincmd l | 1b | tabnext | -tabnew | vertical split | 2b | wincmd l | 3b | tabnext | vertical split | split | 1b | wincmd j | split | 2b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 11` "-c \"echo | vertical split | split | vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 12` "-c \"echo | vertical split | split | vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | vertical split | split | 1b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
+		`#Test case 13` "-c \"echo | -tabnew | vertical split | 2b | wincmd l | 3b | tabnext | vertical split | 2b | wincmd l | 1b | tabdo windo diffthis\" -c \"tabfirst\""
+	)
+
+	local expected_target=(
+		`#Test case 00` "MERGED"
+		`#Test case 01` "MERGED"
+		`#Test case 02` "MERGED"
+		`#Test case 03` "MERGED"
+		`#Test case 04` "MERGED"
+		`#Test case 05` "MERGED"
+		`#Test case 06` "MERGED"
+		`#Test case 07` "MERGED"
+		`#Test case 08` "LOCAL"
+		`#Test case 09` "MERGED"
+		`#Test case 10` "MERGED"
+		`#Test case 11` "MERGED"
+		`#Test case 12` "MERGED"
+		`#Test case 13` "MERGED"
+	)
+
+	local at_least_one_ko="false"
+
+	for i in ${!test_cases[@]}; do
+		gen_cmd "${test_cases[$i]}"
+	
+		if test "$FINAL_CMD" = "${expected_cmd[$i]}" && test "$FINAL_TARGET" = "${expected_target[$i]}"
+		then
+			printf "Test Case #%02d: OK\n" $i
+		else
+			printf "Test Case #%02d: KO !!!!\n" $i
+			echo "  FINAL_CMD              : $FINAL_CMD"
+                        echo "  FINAL_CMD (expected)   : ${expected_cmd[$i]}"
+			echo "  FINAL_TARGET           : $FINAL_TARGET"
+                        echo "  FINAL_TARGET (expected): ${expected_target[$i]}"
+			at_least_one_ko="true"
+		fi
+	done
+	
+	if test "$at_least_one_ko" = "true"
+	then
+		return -1
+	else
+		return 0
+	fi
+}
+
+
+################################################################################
+## API functions (called from "git-mergetool--lib.sh")
+################################################################################
+
 diff_cmd () {
 	"$merge_tool_path" -R -f -d \
 		-c 'wincmd l' -c 'cd $GIT_PREFIX' "$LOCAL" "$REMOTE"
 }
 
+
 merge_cmd () {
+	layout=$(git config mergetool.$merge_tool.layout)
+	print_warning="false"
+
 	case "$1" in
 	*vimdiff)
-		if $base_present
+		if test -z "$layout"
 		then
-			"$merge_tool_path" -f -d -c '4wincmd w | wincmd J' \
-				"$LOCAL" "$BASE" "$REMOTE" "$MERGED"
-		else
-			"$merge_tool_path" -f -d -c 'wincmd l' \
-				"$LOCAL" "$MERGED" "$REMOTE"
+			# Default layout when none is specified
+			layout="(LOCAL | BASE | REMOTE) - MERGED"
 		fi
 		;;
 	*vimdiff1)
-		"$merge_tool_path" -f -d \
-			-c 'echon "Resolve conflicts leftward then save. Use :cq to abort."' \
-			"$LOCAL" "$REMOTE"
-		ret="$?"
-		if test "$ret" -eq 0
-		then
-			cp -- "$LOCAL" "$MERGED"
-		fi
-		return "$ret"
+		layout="LOCAL* | MERGED"
+		print_warning="true"
 		;;
 	*vimdiff2)
-		"$merge_tool_path" -f -d -c 'wincmd l' \
-			"$LOCAL" "$MERGED" "$REMOTE"
+		layout="LOCAL | MERGED | REMOTE"
+		print_warning="true"
 		;;
 	*vimdiff3)
-		if $base_present
-		then
-			"$merge_tool_path" -f -d -c 'hid | hid | hid' \
-				"$LOCAL" "$REMOTE" "$BASE" "$MERGED"
-		else
-			"$merge_tool_path" -f -d -c 'hid | hid' \
-				"$LOCAL" "$REMOTE" "$MERGED"
-		fi
+		layout="MERGED"
+		print_warning="true"
 		;;
 	esac
+
+	if test "$print_warning" = "true"
+	then
+		echo "WARNING:"
+		echo "WARNING: '$1' is going to be removed in a future version. You will be"
+		echo "WARNING: able to obtain the same result by selecting 'vimdiff' as the merge"
+		echo "WARNING: tool and setting configuration variable 'mergetool.vimdiff.layout'"
+		echo "WARNING: to the following value:"
+		echo "WARNING:"
+		echo "WARNING:     layout = \"$layout\""
+		echo "WARNING:"
+		echo "Press ENTER to continue..."
+		read
+	fi
+
+	gen_cmd "$layout"
+
+	debug_print ""
+	debug_print "FINAL CMD : $FINAL_CMD"
+	debug_print "FINAL TAR : $FINAL_TARGET"
+
+	if $base_present
+	then
+		eval "$merge_tool_path" \
+			-f $FINAL_CMD "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
+	else
+		# If there is no BASE (example: a merge conflict in a new file
+		# with the same name created in both braches which didn't exist
+		# before), close all BASE windows using vim's "quit" command
+
+		FINAL_CMD=$(echo $FINAL_CMD | \
+			sed -e 's:2b:quit:g' -e 's:3b:2b:g' -e 's:4b:3b:g')
+
+		eval "$merge_tool_path" \
+			-f $FINAL_CMD "$LOCAL" "$REMOTE" "$MERGED"
+	fi
+
+
+	ret="$?"
+	if test "$ret" -eq 0
+	then
+		if test "$FINAL_TARGET" != "MERGED"
+		then
+			eval cp -- \$"$FINAL_TARGET" "$MERGED"
+		fi
+	fi
+	return "$ret"
 }
 
+
 translate_merge_tool_path() {
 	case "$1" in
 	nvimdiff*)
@@ -57,14 +518,31 @@ translate_merge_tool_path() {
 	esac
 }
 
+
 exit_code_trustable () {
 	true
 }
 
+
 list_tool_variants () {
 	for prefix in '' g n; do
-		for suffix in '' 1 2 3; do
+		for suffix in '' 1 2 3
+		do
 			echo "${prefix}vimdiff${suffix}"
 		done
 	done
 }
+
+
+################################################################################
+## Run unit tests when calling this script from a shell
+################################################################################
+
+if test $(ps -o stat= -p $PPID) = "Ss" && test $(ps -o stat= -p $$) = "S+"
+then
+	# Script is being manually run from command line (see
+	# https://stackoverflow.com/questions/4261876/check-if-bash-script-was-invoked-from-a-shell-or-another-script-application)
+
+	run_unit_tests
+fi
+
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/3] vimdiff: add tool documentation
  2021-11-04 16:09 [PATCH 0/3] vimdiff: new layout option + docs Fernando Ramos
  2021-11-04 16:09 ` [PATCH 1/3] vimdiff: new implementation with layout support Fernando Ramos
@ 2021-11-04 16:09 ` Fernando Ramos
  2021-11-07 21:24   ` David Aguilar
  2021-11-04 16:09 ` [PATCH 3/3] vimdiff: remove deprecated {,g,n}vimdiff{1,2,3} variants Fernando Ramos
  2 siblings, 1 reply; 9+ messages in thread
From: Fernando Ramos @ 2021-11-04 16:09 UTC (permalink / raw)
  To: git; +Cc: gitster, davvid, seth, levraiphilippeblain, rogi

Running 'git {merge,diff}tool --tool-help' now also prints usage
information about the vimdiff tool (and its variantes) instead of just
its name.

Two new functions ('diff_cmd_help()' and 'merge_cmd_help()') have been
added to the set of functions that each merge tool (ie. scripts found
inside "mergetools/") can overwrite to provided tool specific
information.

Right now, only 'mergetools/vimdiff' implements these functions, but
other tools are encouraged to do so in the future, specially if they
take configuration options not explained anywhere else (as it is the
case with the 'vimdiff' tool and the new 'layout' option)

Signed-off-by: Fernando Ramos <greenfoo@u92.eu>
---
 git-mergetool--lib.sh |  12 ++
 mergetools/vimdiff    | 272 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 284 insertions(+)

diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index 542a6a75eb..3964cd8f99 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -64,6 +64,10 @@ $(list_tool_variants)"
 				fi
 				shown_any=yes
 				printf "%s%s\n" "$per_line_prefix" "$toolname"
+				while IFS= read -r line
+				do
+					printf "%s\t%s\n" "$per_line_prefix" "$line"
+                                done < <(diff_mode && diff_cmd_help "$toolname" || merge_cmd_help "$toolname")
 			fi
 		done
 
@@ -162,10 +166,18 @@ setup_tool () {
 		return 1
 	}
 
+	diff_cmd_help () {
+		return 0
+	}
+
 	merge_cmd () {
 		return 1
 	}
 
+	merge_cmd_help () {
+		return 0
+	}
+
 	hide_resolved_enabled () {
 		return 0
 	}
diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index 1f2e88777e..aa8fbc0a19 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -428,6 +428,46 @@ diff_cmd () {
 		-c 'wincmd l' -c 'cd $GIT_PREFIX' "$LOCAL" "$REMOTE"
 }
 
+diff_cmd_help() {
+	case "$1" in
+	vimdiff)
+		cat <<-ENDOFMESSAGE
+			Opens vim with two vertical windows: LOCAL changes will be placed in the left
+			window and REMOTE changes in the right one.
+		ENDOFMESSAGE
+		;;
+	vimdiff*)
+		cat <<-ENDOFMESSAGE
+			Same as 'vimdiff'
+		ENDOFMESSAGE
+		;;
+	gvimdiff)
+		cat <<-ENDOFMESSAGE
+			Same as 'vimdiff' but opens 'gvim' instead (which uses a graphical toolkit for
+			opening its own window)
+		ENDOFMESSAGE
+		;;
+	gvimdiff*)
+		cat <<-ENDOFMESSAGE
+			Same as 'gvimdiff'
+		ENDOFMESSAGE
+		;;
+	nvimdiff)
+		cat <<-ENDOFMESSAGE
+			Same as 'vimdiff' but opens 'neovim' instead (which is a fork of the original
+			'vim' 'focused on extensibility and usability' according to their authors)
+		ENDOFMESSAGE
+		;;
+	nvimdiff*)
+		cat <<-ENDOFMESSAGE
+			Same as 'nvimdiff'
+		ENDOFMESSAGE
+		;;
+	esac
+
+	return 0
+}
+
 
 merge_cmd () {
 	layout=$(git config mergetool.$merge_tool.layout)
@@ -503,6 +543,238 @@ merge_cmd () {
 	return "$ret"
 }
 
+merge_cmd_help() {
+	case "$1" in
+	vimdiff)
+		cat <<-ENDOFMESSAGE
+			Opens vim with a 4 windows layout distributed in the following way:
+			
+			    ------------------------------------------
+			    |             |           |              |
+			    |   LOCAL     |   BASE    |   REMOTE     |
+			    |             |           |              |
+			    ------------------------------------------
+			    |                                        |
+			    |                MERGED                  |
+			    |                                        |
+			    ------------------------------------------
+			
+			"LOCAL", "BASE" and "REMOTE" are read-only buffers showing the contents of the
+			conflicting file in a specific git commit ("commit you are merging into",
+			"common ancestor commit" and "commit you are merging from" respectively)
+			
+			"MERGED" is a writable buffer where you have to resolve the conflicts (using the
+			other read-only buffers as a reference). Once you are done, save and exit "vim"
+			as usual (":wq") or, if you want to abort, exit using ":cq".
+			
+			You can change the windows layout used by vim by setting configuration variable
+			"mergetool.vimdiff.layout" which accepts a string where these separators have
+			special meaning:
+			
+			  - ";" is used to "open a new tab"
+			  - "-" is used to "open a new vertical split"
+			  - "|" is used to "open a new horizontal split"
+			
+			Let's see some examples to undertand how it works:
+			
+			  * layout = "(LOCAL | BASE | REMOTE) - MERGED"
+			
+			    This is exactly the same as the default layout we have already seen.
+			
+			  * layout = "LOCAL | MERGED | REMOTE"
+			
+			    If, for some reason, we are not interested in the "BASE" buffer.
+			
+			           ------------------------------------------
+			           |             |           |              |
+			           |             |           |              |
+			           |   LOCAL     |   MERGED  |   REMOTE     |
+			           |             |           |              |
+			           |             |           |              |
+			           ------------------------------------------
+			
+			  * layout = "MERGED"
+			
+			    Only the "MERGED" buffer will be shown. Note, however, that all the other
+			    ones are still loaded in vim, and you can access them with the "buffers"
+			    command. 
+			
+			           ------------------------------------------
+			           |                                        |
+			           |                                        |
+			           |                 MERGED                 |
+			           |                                        |
+			           |                                        |
+			           ------------------------------------------
+			
+			  * layout = "LOCAL* | REMOTE"
+			
+			    When "MERGED" is not present in the layout, you must "mark" one of the
+			    buffers with an asterisk. That will become the buffer you need to edit and
+			    save after resolving the conflicts.
+			
+			           ------------------------------------------
+			           |                   |                    |
+			           |                   |                    |
+			           |                   |                    |
+			           |     LOCAL         |    REMOTE          |
+			           |                   |                    |
+			           |                   |                    |
+			           |                   |                    |
+			           ------------------------------------------
+			
+                          * layout = "(LOCAL | BASE | REMOTE) - MERGED; BASE | LOCAL; BASE | REMOTE"
+			
+			    Three tabs will open: the first one is a copy of the default layout, while
+			    the other two only show the differences between "BASE" and "LOCAL" and
+			    "BASE" and "REMOTE" respectively.
+			 
+			           ------------------------------------------
+			           | <TAB #1> |  TAB #2  |  TAB #3  |       |
+			           ------------------------------------------
+			           |             |           |              |
+			           |   LOCAL     |   BASE    |   REMOTE     |
+			           |             |           |              |
+			           ------------------------------------------
+			           |                                        |
+			           |                MERGED                  |
+			           |                                        |
+			           ------------------------------------------
+			
+			           ------------------------------------------
+			           |  TAB #1  | <TAB #2> |  TAB #3  |       |
+			           ------------------------------------------
+			           |                   |                    |
+			           |                   |                    |
+			           |                   |                    |
+			           |     BASE          |    LOCAL           |
+			           |                   |                    |
+			           |                   |                    |
+			           |                   |                    |
+			           ------------------------------------------
+			
+			           ------------------------------------------
+			           |  TAB #1  |  TAB #2  | <TAB #3> |       |
+			           ------------------------------------------
+			           |                   |                    |
+			           |                   |                    |
+			           |                   |                    |
+			           |     BASE          |    REMOTE          |
+			           |                   |                    |
+			           |                   |                    |
+			           |                   |                    |
+			           ------------------------------------------
+			
+                          * layout = "(LOCAL | BASE | REMOTE) - MERGED; BASE | LOCAL; BASE | REMOTE; (LOCAL - BASE - REMOTE) | MERGED"
+			
+			    Same as the previous example, but adds a fourth tab with the same
+			    information as the first tab, with a different layout.
+			 
+			           ---------------------------------------------
+			           |  TAB #1  |  TAB #2  |  TAB #3  | <TAB #4> |
+			           ---------------------------------------------
+			           |       LOCAL         |                     |
+			           |---------------------|                     |
+			           |       BASE          |        MERGED       |
+			           |---------------------|                     |
+			           |       REMOTE        |                     |
+			           ---------------------------------------------
+
+		ENDOFMESSAGE
+		;;
+	vimdiff1)
+		cat <<-ENDOFMESSAGE
+			Same as 'vimdiff' using this layout: "LOCAL* | REMOTE"
+			
+			This will probably be deprecated in the future. Please use "vimdiff" and
+			manually set the "mergetool.vimdiff.layout" configuration variable instead.
+		ENDOFMESSAGE
+		;;
+	vimdiff2)
+		cat <<-ENDOFMESSAGE
+			Same as 'vimdiff' using this layout: "LOCAL | MERGED | REMOTE"
+			
+			This will probably be deprecated in the future. Please use "vimdiff" and
+			manually set the "mergetool.vimdiff.layout" configuration variable instead.
+		ENDOFMESSAGE
+		;;
+	vimdiff3)
+		cat <<-ENDOFMESSAGE
+			Same as 'vimdiff' using this layout: "MERGED"
+			
+			This will probably be deprecated in the future. Please use "vimdiff" and
+			manually set the "mergetool.vimdiff.layout" configuration variable instead.
+		ENDOFMESSAGE
+		;;
+	gvimdiff)
+		cat <<-ENDOFMESSAGE
+			Same as 'vimdiff' but opens 'gvim' instead (which uses a graphical toolkit for
+			opening its own window).
+			The desired layout can be set with configuration variable
+			"mergetool.gvimdiff.layout"
+		ENDOFMESSAGE
+		;;
+	gvimdiff1)
+		cat <<-ENDOFMESSAGE
+			Same as 'gvimdiff' using this layout: "LOCAL* | REMOTE"
+			
+			This will probably be deprecated in the future. Please use "gvimdiff" and
+			manually set the "mergetool.gvimdiff.layout" configuration variable instead.
+		ENDOFMESSAGE
+		;;
+	gvimdiff2)
+		cat <<-ENDOFMESSAGE
+			Same as 'gvimdiff' using this layout: "LOCAL | MERGED | REMOTE"
+			
+			This will probably be deprecated in the future. Please use "gvimdiff" and
+			manually set the "mergetool.gvimdiff.layout" configuration variable instead.
+		ENDOFMESSAGE
+		;;
+	gvimdiff3)
+		cat <<-ENDOFMESSAGE
+			Same as 'gvimdiff' using this layout: "MERGED"
+			
+			This will probably be deprecated in the future. Please use "gvimdiff" and
+			manually set the "mergetool.gvimdiff.layout" configuration variable instead.
+		ENDOFMESSAGE
+		;;
+	nvimdiff)
+		cat <<-ENDOFMESSAGE
+			Same as 'vimdiff' but opens 'neovim' instead (which is a fork of the original
+			'vim' 'focused on extensibility and usability' according to their authors)
+			The desired layout can be set with configuration variable
+			"mergetool.nvimdiff.layout"
+		ENDOFMESSAGE
+		;;
+	nvimdiff1)
+		cat <<-ENDOFMESSAGE
+			Same as 'nvimdiff' using this layout: "LOCAL* | REMOTE"
+			
+			This will probably be deprecated in the future. Please use "nvimdiff" and
+			manually set the "mergetool.nvimdiff.layout" configuration variable instead.
+		ENDOFMESSAGE
+		;;
+	nvimdiff2)
+		cat <<-ENDOFMESSAGE
+			Same as 'nvimdiff' using this layout: "LOCAL | MERGED | REMOTE"
+			
+			This will probably be deprecated in the future. Please use "nvimdiff" and
+			manually set the "mergetool.nvimdiff.layout" configuration variable instead.
+		ENDOFMESSAGE
+		;;
+	nvimdiff3)
+		cat <<-ENDOFMESSAGE
+			Same as 'nvimdiff' using this layout: "MERGED"
+			
+			This will probably be deprecated in the future. Please use "nvimdiff" and
+			manually set the "mergetool.nvimdiff.layout" configuration variable instead.
+		ENDOFMESSAGE
+		;;
+	esac
+
+	return 0
+}
+
 
 translate_merge_tool_path() {
 	case "$1" in
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/3] vimdiff: remove deprecated {,g,n}vimdiff{1,2,3} variants
  2021-11-04 16:09 [PATCH 0/3] vimdiff: new layout option + docs Fernando Ramos
  2021-11-04 16:09 ` [PATCH 1/3] vimdiff: new implementation with layout support Fernando Ramos
  2021-11-04 16:09 ` [PATCH 2/3] vimdiff: add tool documentation Fernando Ramos
@ 2021-11-04 16:09 ` Fernando Ramos
  2 siblings, 0 replies; 9+ messages in thread
From: Fernando Ramos @ 2021-11-04 16:09 UTC (permalink / raw)
  To: git; +Cc: gitster, davvid, seth, levraiphilippeblain, rogi

After this commit is merged, users of "{,g,n}vimdiff{1,2,3}" will need to
set their merge tool to "{,g,v}vimdiff" (without the number suffix) and
the "mergetool.{,g,n}vimdiff.layout" configuration option to one of
these:

  * For "1" variant: "LOCAL* | REMOTE"
  * For "2" variant: "LOCAL | MERGED | REMOTE"
  * For "3" variant: "MERGED"

Signed-off-by: Fernando Ramos <greenfoo@u92.eu>
---
 mergetools/vimdiff | 125 ++-------------------------------------------
 1 file changed, 4 insertions(+), 121 deletions(-)

diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index aa8fbc0a19..9d469c0553 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -436,33 +436,18 @@ diff_cmd_help() {
 			window and REMOTE changes in the right one.
 		ENDOFMESSAGE
 		;;
-	vimdiff*)
-		cat <<-ENDOFMESSAGE
-			Same as 'vimdiff'
-		ENDOFMESSAGE
-		;;
 	gvimdiff)
 		cat <<-ENDOFMESSAGE
 			Same as 'vimdiff' but opens 'gvim' instead (which uses a graphical toolkit for
 			opening its own window)
 		ENDOFMESSAGE
 		;;
-	gvimdiff*)
-		cat <<-ENDOFMESSAGE
-			Same as 'gvimdiff'
-		ENDOFMESSAGE
-		;;
 	nvimdiff)
 		cat <<-ENDOFMESSAGE
 			Same as 'vimdiff' but opens 'neovim' instead (which is a fork of the original
 			'vim' 'focused on extensibility and usability' according to their authors)
 		ENDOFMESSAGE
 		;;
-	nvimdiff*)
-		cat <<-ENDOFMESSAGE
-			Same as 'nvimdiff'
-		ENDOFMESSAGE
-		;;
 	esac
 
 	return 0
@@ -471,7 +456,6 @@ diff_cmd_help() {
 
 merge_cmd () {
 	layout=$(git config mergetool.$merge_tool.layout)
-	print_warning="false"
 
 	case "$1" in
 	*vimdiff)
@@ -481,34 +465,8 @@ merge_cmd () {
 			layout="(LOCAL | BASE | REMOTE) - MERGED"
 		fi
 		;;
-	*vimdiff1)
-		layout="LOCAL* | MERGED"
-		print_warning="true"
-		;;
-	*vimdiff2)
-		layout="LOCAL | MERGED | REMOTE"
-		print_warning="true"
-		;;
-	*vimdiff3)
-		layout="MERGED"
-		print_warning="true"
-		;;
 	esac
 
-	if test "$print_warning" = "true"
-	then
-		echo "WARNING:"
-		echo "WARNING: '$1' is going to be removed in a future version. You will be"
-		echo "WARNING: able to obtain the same result by selecting 'vimdiff' as the merge"
-		echo "WARNING: tool and setting configuration variable 'mergetool.vimdiff.layout'"
-		echo "WARNING: to the following value:"
-		echo "WARNING:"
-		echo "WARNING:     layout = \"$layout\""
-		echo "WARNING:"
-		echo "Press ENTER to continue..."
-		read
-	fi
-
 	gen_cmd "$layout"
 
 	debug_print ""
@@ -682,30 +640,6 @@ merge_cmd_help() {
 
 		ENDOFMESSAGE
 		;;
-	vimdiff1)
-		cat <<-ENDOFMESSAGE
-			Same as 'vimdiff' using this layout: "LOCAL* | REMOTE"
-			
-			This will probably be deprecated in the future. Please use "vimdiff" and
-			manually set the "mergetool.vimdiff.layout" configuration variable instead.
-		ENDOFMESSAGE
-		;;
-	vimdiff2)
-		cat <<-ENDOFMESSAGE
-			Same as 'vimdiff' using this layout: "LOCAL | MERGED | REMOTE"
-			
-			This will probably be deprecated in the future. Please use "vimdiff" and
-			manually set the "mergetool.vimdiff.layout" configuration variable instead.
-		ENDOFMESSAGE
-		;;
-	vimdiff3)
-		cat <<-ENDOFMESSAGE
-			Same as 'vimdiff' using this layout: "MERGED"
-			
-			This will probably be deprecated in the future. Please use "vimdiff" and
-			manually set the "mergetool.vimdiff.layout" configuration variable instead.
-		ENDOFMESSAGE
-		;;
 	gvimdiff)
 		cat <<-ENDOFMESSAGE
 			Same as 'vimdiff' but opens 'gvim' instead (which uses a graphical toolkit for
@@ -714,30 +648,6 @@ merge_cmd_help() {
 			"mergetool.gvimdiff.layout"
 		ENDOFMESSAGE
 		;;
-	gvimdiff1)
-		cat <<-ENDOFMESSAGE
-			Same as 'gvimdiff' using this layout: "LOCAL* | REMOTE"
-			
-			This will probably be deprecated in the future. Please use "gvimdiff" and
-			manually set the "mergetool.gvimdiff.layout" configuration variable instead.
-		ENDOFMESSAGE
-		;;
-	gvimdiff2)
-		cat <<-ENDOFMESSAGE
-			Same as 'gvimdiff' using this layout: "LOCAL | MERGED | REMOTE"
-			
-			This will probably be deprecated in the future. Please use "gvimdiff" and
-			manually set the "mergetool.gvimdiff.layout" configuration variable instead.
-		ENDOFMESSAGE
-		;;
-	gvimdiff3)
-		cat <<-ENDOFMESSAGE
-			Same as 'gvimdiff' using this layout: "MERGED"
-			
-			This will probably be deprecated in the future. Please use "gvimdiff" and
-			manually set the "mergetool.gvimdiff.layout" configuration variable instead.
-		ENDOFMESSAGE
-		;;
 	nvimdiff)
 		cat <<-ENDOFMESSAGE
 			Same as 'vimdiff' but opens 'neovim' instead (which is a fork of the original
@@ -746,30 +656,6 @@ merge_cmd_help() {
 			"mergetool.nvimdiff.layout"
 		ENDOFMESSAGE
 		;;
-	nvimdiff1)
-		cat <<-ENDOFMESSAGE
-			Same as 'nvimdiff' using this layout: "LOCAL* | REMOTE"
-			
-			This will probably be deprecated in the future. Please use "nvimdiff" and
-			manually set the "mergetool.nvimdiff.layout" configuration variable instead.
-		ENDOFMESSAGE
-		;;
-	nvimdiff2)
-		cat <<-ENDOFMESSAGE
-			Same as 'nvimdiff' using this layout: "LOCAL | MERGED | REMOTE"
-			
-			This will probably be deprecated in the future. Please use "nvimdiff" and
-			manually set the "mergetool.nvimdiff.layout" configuration variable instead.
-		ENDOFMESSAGE
-		;;
-	nvimdiff3)
-		cat <<-ENDOFMESSAGE
-			Same as 'nvimdiff' using this layout: "MERGED"
-			
-			This will probably be deprecated in the future. Please use "nvimdiff" and
-			manually set the "mergetool.nvimdiff.layout" configuration variable instead.
-		ENDOFMESSAGE
-		;;
 	esac
 
 	return 0
@@ -778,13 +664,13 @@ merge_cmd_help() {
 
 translate_merge_tool_path() {
 	case "$1" in
-	nvimdiff*)
+	nvimdiff)
 		echo nvim
 		;;
-	gvimdiff*)
+	gvimdiff)
 		echo gvim
 		;;
-	vimdiff*)
+	vimdiff)
 		echo vim
 		;;
 	esac
@@ -798,10 +684,7 @@ exit_code_trustable () {
 
 list_tool_variants () {
 	for prefix in '' g n; do
-		for suffix in '' 1 2 3
-		do
-			echo "${prefix}vimdiff${suffix}"
-		done
+		echo "${prefix}vimdiff"
 	done
 }
 
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/3] vimdiff: add tool documentation
  2021-11-04 16:09 ` [PATCH 2/3] vimdiff: add tool documentation Fernando Ramos
@ 2021-11-07 21:24   ` David Aguilar
  2021-11-08  1:02     ` Eric Sunshine
  2021-11-08 19:08     ` Junio C Hamano
  0 siblings, 2 replies; 9+ messages in thread
From: David Aguilar @ 2021-11-07 21:24 UTC (permalink / raw)
  To: Fernando Ramos
  Cc: Git Mailing List, Junio C Hamano, Seth House, levraiphilippeblain,
	rogi

Thanks for writing this up. Notes inline below.


On Thu, Nov 4, 2021 at 9:10 AM Fernando Ramos <greenfoo@u92.eu> wrote:
>
> Running 'git {merge,diff}tool --tool-help' now also prints usage
> information about the vimdiff tool (and its variantes) instead of just
> its name.
>
> Two new functions ('diff_cmd_help()' and 'merge_cmd_help()') have been
> added to the set of functions that each merge tool (ie. scripts found
> inside "mergetools/") can overwrite to provided tool specific
> information.
>
> Right now, only 'mergetools/vimdiff' implements these functions, but
> other tools are encouraged to do so in the future, specially if they
> take configuration options not explained anywhere else (as it is the
> case with the 'vimdiff' tool and the new 'layout' option)
>
> Signed-off-by: Fernando Ramos <greenfoo@u92.eu>
> ---
>  git-mergetool--lib.sh |  12 ++
>  mergetools/vimdiff    | 272 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 284 insertions(+)
>
> diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
> index 542a6a75eb..3964cd8f99 100644
> --- a/git-mergetool--lib.sh
> +++ b/git-mergetool--lib.sh
> @@ -64,6 +64,10 @@ $(list_tool_variants)"
>                                 fi
>                                 shown_any=yes
>                                 printf "%s%s\n" "$per_line_prefix" "$toolname"
> +                               while IFS= read -r line
> +                               do
> +                                       printf "%s\t%s\n" "$per_line_prefix" "$line"
> +                                done < <(diff_mode && diff_cmd_help "$toolname" || merge_cmd_help "$toolname")


If we wanted to shorten this line a bit, would it work to run the pipeline
first and pipe the result?

    (diff_mode && ... || merge_cmd_help ...) |
    while IFS= read -r line
    do
       ...
    done

How come we have to unset IFS here?
We often do:

IFS='
'

so that we have just \n newline as the separator.

I wonder if that would be better along with a save/restore of the IFS value?


>                         fi
>                 done
>
> @@ -162,10 +166,18 @@ setup_tool () {
>                 return 1
>         }
>
> +       diff_cmd_help () {
> +               return 0
> +       }
> +
>         merge_cmd () {
>                 return 1
>         }
>
> +       merge_cmd_help () {
> +               return 0
> +       }
> +
>         hide_resolved_enabled () {
>                 return 0
>         }
> diff --git a/mergetools/vimdiff b/mergetools/vimdiff
> index 1f2e88777e..aa8fbc0a19 100644
> --- a/mergetools/vimdiff
> +++ b/mergetools/vimdiff
> @@ -428,6 +428,46 @@ diff_cmd () {
>                 -c 'wincmd l' -c 'cd $GIT_PREFIX' "$LOCAL" "$REMOTE"
>  }
>
> +diff_cmd_help() {
> +       case "$1" in
> +       vimdiff)
> +               cat <<-ENDOFMESSAGE
> +                       Opens vim with two vertical windows: LOCAL changes will be placed in the left
> +                       window and REMOTE changes in the right one.
> +               ENDOFMESSAGE


Tiny nit: we call this EOF in the other places
(git-mergetool--helper.sh) where we do the same.

ENDOFMESSAGE is a bit verbose so it might be worth sticking to the
conventional EOF marker.



> +               ;;
> +       vimdiff*)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'vimdiff'
> +               ENDOFMESSAGE
> +               ;;
> +       gvimdiff)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'vimdiff' but opens 'gvim' instead (which uses a graphical toolkit for
> +                       opening its own window)
> +               ENDOFMESSAGE
> +               ;;
> +       gvimdiff*)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'gvimdiff'
> +               ENDOFMESSAGE
> +               ;;
> +       nvimdiff)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'vimdiff' but opens 'neovim' instead (which is a fork of the original
> +                       'vim' 'focused on extensibility and usability' according to their authors)
> +               ENDOFMESSAGE
> +               ;;
> +       nvimdiff*)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'nvimdiff'
> +               ENDOFMESSAGE
> +               ;;
> +       esac
> +
> +       return 0
> +}
> +
>
>  merge_cmd () {
>         layout=$(git config mergetool.$merge_tool.layout)
> @@ -503,6 +543,238 @@ merge_cmd () {
>         return "$ret"
>  }
>
> +merge_cmd_help() {
> +       case "$1" in
> +       vimdiff)
> +               cat <<-ENDOFMESSAGE
> +                       Opens vim with a 4 windows layout distributed in the following way:
> +
> +                           ------------------------------------------
> +                           |             |           |              |
> +                           |   LOCAL     |   BASE    |   REMOTE     |
> +                           |             |           |              |
> +                           ------------------------------------------
> +                           |                                        |
> +                           |                MERGED                  |
> +                           |                                        |
> +                           ------------------------------------------
> +
> +                       "LOCAL", "BASE" and "REMOTE" are read-only buffers showing the contents of the
> +                       conflicting file in a specific git commit ("commit you are merging into",
> +                       "common ancestor commit" and "commit you are merging from" respectively)
> +
> +                       "MERGED" is a writable buffer where you have to resolve the conflicts (using the
> +                       other read-only buffers as a reference). Once you are done, save and exit "vim"
> +                       as usual (":wq") or, if you want to abort, exit using ":cq".
> +
> +                       You can change the windows layout used by vim by setting configuration variable
> +                       "mergetool.vimdiff.layout" which accepts a string where these separators have
> +                       special meaning:
> +
> +                         - ";" is used to "open a new tab"
> +                         - "-" is used to "open a new vertical split"
> +                         - "|" is used to "open a new horizontal split"


This would probably be a good place to mention the "*" current marker as well.

I wonder how good these special symbols are for shell friendliness.

Instead of a "*" suffix I would suggest a "@" prefix eg. "@LOCAL" for
the "use this tab" stuff.



The "|" is a nice choice visually, as is ";" but I wonder if we can
avoid shell metacharacters.

Instead of ";" would "+" or "T" work for "open a new tab". I kinda
like "+", so my examples below will use that.



Instead of "|" would "," (comma) work for a vertical split? Ditto --
the examples below will use "," comma.

Note that I said "vertical", which is the opposite of what is written
above (horizontal split) for "|".

I believe these are called "vertical" splits:

   LOCAL | BASE | REMOTE

because the splits are vertical... that might just be a small
switcheroo in the docs that needs updating.



"-" is nice for horizontal splits. It visually matches what happens,
but in the context of "," and "+" I think "/" would be a better
choice.

"a / b" means "a over b" in some math interpretations, and visually
would make sense for horizontal splits where "a" is on the top and "b"
is on the bottom.


Anyways, just some sugs to try and avoid shell syntax. My examples
below will use "/" for horizontal splits.


> +
> +                       Let's see some examples to undertand how it works:
> +
> +                         * layout = "(LOCAL | BASE | REMOTE) - MERGED"


My sug would be to write this as either: "LOCAL,BASE,REMOTE - MERGED"
or "(LOCAL, BASE, REMOTE) - MERGED".

Are the (parens) necessary, or can we infer grouping because
"LOCAL,BASE,REMOTE" are grouped together in the first example? Maybe
that's too subtle, but it's an idea.


> +
> +                           This is exactly the same as the default layout we have already seen.
> +
> +                         * layout = "LOCAL | MERGED | REMOTE"


My sug would be: "LOCAL, MERGED, REMOTE".

> +
> +                           If, for some reason, we are not interested in the "BASE" buffer.
> +
> +                                  ------------------------------------------
> +                                  |             |           |              |
> +                                  |             |           |              |
> +                                  |   LOCAL     |   MERGED  |   REMOTE     |
> +                                  |             |           |              |
> +                                  |             |           |              |
> +                                  ------------------------------------------
> +
> +                         * layout = "MERGED"
> +
> +                           Only the "MERGED" buffer will be shown. Note, however, that all the other
> +                           ones are still loaded in vim, and you can access them with the "buffers"
> +                           command.
> +
> +                                  ------------------------------------------
> +                                  |                                        |
> +                                  |                                        |
> +                                  |                 MERGED                 |
> +                                  |                                        |
> +                                  |                                        |
> +                                  ------------------------------------------
> +
> +                         * layout = "LOCAL* | REMOTE"
> +
> +                           When "MERGED" is not present in the layout, you must "mark" one of the
> +                           buffers with an asterisk. That will become the buffer you need to edit and
> +                           save after resolving the conflicts.


My sug is that this would be written as: "@LOCAL, REMOTE"


> +
> +                                  ------------------------------------------
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  |     LOCAL         |    REMOTE          |
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  ------------------------------------------
> +
> +                          * layout = "(LOCAL | BASE | REMOTE) - MERGED; BASE | LOCAL; BASE | REMOTE"


If we eschewed parens then this would look like:

    "LOCAL,BASE,REMOTE / MERGED + BASE,LOCAL + BASE,REMOTE"

With parens it would be:

    "(LOCAL, BASE, REMOTE) / MERGED + BASE, LOCAL + BASE, REMOTE"

I personally like the idea of not needing the parens if we can do without them,
but I haven't looked at the implementation at all yet.

I suspect the more complicated examples below require them, perhaps?

The upside of parens is that it avoids having significant whitespace,
so a grouping syntax might be warranted.


> +
> +                           Three tabs will open: the first one is a copy of the default layout, while
> +                           the other two only show the differences between "BASE" and "LOCAL" and
> +                           "BASE" and "REMOTE" respectively.
> +
> +                                  ------------------------------------------
> +                                  | <TAB #1> |  TAB #2  |  TAB #3  |       |
> +                                  ------------------------------------------
> +                                  |             |           |              |
> +                                  |   LOCAL     |   BASE    |   REMOTE     |
> +                                  |             |           |              |
> +                                  ------------------------------------------
> +                                  |                                        |
> +                                  |                MERGED                  |
> +                                  |                                        |
> +                                  ------------------------------------------
> +
> +                                  ------------------------------------------
> +                                  |  TAB #1  | <TAB #2> |  TAB #3  |       |
> +                                  ------------------------------------------
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  |     BASE          |    LOCAL           |
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  ------------------------------------------
> +
> +                                  ------------------------------------------
> +                                  |  TAB #1  |  TAB #2  | <TAB #3> |       |
> +                                  ------------------------------------------
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  |     BASE          |    REMOTE          |
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  |                   |                    |
> +                                  ------------------------------------------
> +
> +                          * layout = "(LOCAL | BASE | REMOTE) - MERGED; BASE | LOCAL; BASE | REMOTE; (LOCAL - BASE - REMOTE) | MERGED"


This would be:

"(LOCAL, BASE, REMOTE) / MERGED + BASE, LOCAL + BASE, REMOTE + (LOCAL
/ BASE / REMOTE), MERGED"

Without parents it would be:

"LOCAL,BASE,REMOTE / MERGED + BASE, LOCAL + BASE, REMOTE +
LOCAL/BASE/REMOTE, MERGED"


That does seem a bit subtle... it could work, but maybe we should
avoid making whitespace have special meaning and just go with parens
(or [] or {}) for grouping.

I'd like to hear some opinions on that.


> +
> +                           Same as the previous example, but adds a fourth tab with the same
> +                           information as the first tab, with a different layout.
> +
> +                                  ---------------------------------------------
> +                                  |  TAB #1  |  TAB #2  |  TAB #3  | <TAB #4> |
> +                                  ---------------------------------------------
> +                                  |       LOCAL         |                     |
> +                                  |---------------------|                     |
> +                                  |       BASE          |        MERGED       |
> +                                  |---------------------|                     |
> +                                  |       REMOTE        |                     |
> +                                  ---------------------------------------------
> +
> +               ENDOFMESSAGE
> +               ;;


This example really shows off how nice and flexible this approach is.
This is really cool, by the way.

My only other sug is that maybe this documentation can be in the
Documentation/ area and these messages can be reduced to something
along the lines of:

    Please see the "vimdiff" section in "git help mergetool" or "git
help difftool" for more details.


Not sure. It is kinda nice that the help message is maximally helpful as-is,
but it probably should be in Documentation/ so that we don't have to duplicate
the information in multiple places.



> +       vimdiff1)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'vimdiff' using this layout: "LOCAL* | REMOTE"
> +
> +                       This will probably be deprecated in the future. Please use "vimdiff" and
> +                       manually set the "mergetool.vimdiff.layout" configuration variable instead.
> +               ENDOFMESSAGE
> +               ;;
> +       vimdiff2)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'vimdiff' using this layout: "LOCAL | MERGED | REMOTE"
> +
> +                       This will probably be deprecated in the future. Please use "vimdiff" and
> +                       manually set the "mergetool.vimdiff.layout" configuration variable instead.
> +               ENDOFMESSAGE
> +               ;;
> +       vimdiff3)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'vimdiff' using this layout: "MERGED"
> +
> +                       This will probably be deprecated in the future. Please use "vimdiff" and
> +                       manually set the "mergetool.vimdiff.layout" configuration variable instead.
> +               ENDOFMESSAGE
> +               ;;
> +       gvimdiff)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'vimdiff' but opens 'gvim' instead (which uses a graphical toolkit for
> +                       opening its own window).
> +                       The desired layout can be set with configuration variable
> +                       "mergetool.gvimdiff.layout"
> +               ENDOFMESSAGE
> +               ;;
> +       gvimdiff1)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'gvimdiff' using this layout: "LOCAL* | REMOTE"
> +
> +                       This will probably be deprecated in the future. Please use "gvimdiff" and
> +                       manually set the "mergetool.gvimdiff.layout" configuration variable instead.
> +               ENDOFMESSAGE
> +               ;;
> +       gvimdiff2)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'gvimdiff' using this layout: "LOCAL | MERGED | REMOTE"
> +
> +                       This will probably be deprecated in the future. Please use "gvimdiff" and
> +                       manually set the "mergetool.gvimdiff.layout" configuration variable instead.
> +               ENDOFMESSAGE
> +               ;;
> +       gvimdiff3)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'gvimdiff' using this layout: "MERGED"
> +
> +                       This will probably be deprecated in the future. Please use "gvimdiff" and
> +                       manually set the "mergetool.gvimdiff.layout" configuration variable instead.
> +               ENDOFMESSAGE
> +               ;;
> +       nvimdiff)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'vimdiff' but opens 'neovim' instead (which is a fork of the original
> +                       'vim' 'focused on extensibility and usability' according to their authors)
> +                       The desired layout can be set with configuration variable
> +                       "mergetool.nvimdiff.layout"
> +               ENDOFMESSAGE
> +               ;;
> +       nvimdiff1)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'nvimdiff' using this layout: "LOCAL* | REMOTE"
> +
> +                       This will probably be deprecated in the future. Please use "nvimdiff" and
> +                       manually set the "mergetool.nvimdiff.layout" configuration variable instead.
> +               ENDOFMESSAGE
> +               ;;
> +       nvimdiff2)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'nvimdiff' using this layout: "LOCAL | MERGED | REMOTE"
> +
> +                       This will probably be deprecated in the future. Please use "nvimdiff" and
> +                       manually set the "mergetool.nvimdiff.layout" configuration variable instead.
> +               ENDOFMESSAGE
> +               ;;
> +       nvimdiff3)
> +               cat <<-ENDOFMESSAGE
> +                       Same as 'nvimdiff' using this layout: "MERGED"
> +
> +                       This will probably be deprecated in the future. Please use "nvimdiff" and
> +                       manually set the "mergetool.nvimdiff.layout" configuration variable instead.
> +               ENDOFMESSAGE
> +               ;;
> +       esac
> +
> +       return 0
> +}
> +
>
>  translate_merge_tool_path() {
>         case "$1" in
> --
> 2.33.1
>

Thanks for taking it this far! I personally like the direction this is
going, but I'd very much appreciate hearing others' opinions.
-- 
David

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] vimdiff: new implementation with layout support
  2021-11-04 16:09 ` [PATCH 1/3] vimdiff: new implementation with layout support Fernando Ramos
@ 2021-11-07 22:41   ` David Aguilar
  2021-11-08  0:47     ` Eric Sunshine
  0 siblings, 1 reply; 9+ messages in thread
From: David Aguilar @ 2021-11-07 22:41 UTC (permalink / raw)
  To: Fernando Ramos
  Cc: Git Mailing List, Junio C Hamano, Seth House, levraiphilippeblain,
	rogi

This is an early review. We're still discussing the docs but there's a
few things worth mentioning now before we go too far.

On Thu, Nov 4, 2021 at 9:10 AM Fernando Ramos <greenfoo@u92.eu> wrote:
>
> When running 'git mergetool -t vimdiff', a new configuration option
> ('mergetool.vimdiff.layout') can now be used to select how the user
> wants the different windows, tabs and buffers to be displayed.
>
> If the option is not provided, the layout will be the same one that was
> being used before this commit (ie. two rows with LOCAL, BASE and COMMIT
> in the top one and MERGED in the bottom one).
>
> The 'vimdiff' variants ('vimdiff{1,2,3}') still work but, because they
> represented nothing else than different layouts, are now internally
> implemented as a subcase of 'vimdiff' with the corresponding
> pre-configured 'layout'.
>
> Signed-off-by: Fernando Ramos <greenfoo@u92.eu>
> ---
>  mergetools/vimdiff | 530 ++++++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 504 insertions(+), 26 deletions(-)
>
> diff --git a/mergetools/vimdiff b/mergetools/vimdiff
> index 96f6209a04..1f2e88777e 100644
> --- a/mergetools/vimdiff
> +++ b/mergetools/vimdiff
> @@ -1,48 +1,509 @@
> +#!/bin/bash
> +
> +# This script can be run in two different contexts:
> +#
> +#   - From git, when the user invokes the "vimdiff" merge tool. In this context
> +#     this script expects the following environment variables (among others) to
> +#     be defined (which is something "git" takes care of):
> +#
> +#       - $BASE
> +#       - $LOCAL
> +#       - $REMOTE
> +#       - $MERGED
> +#
> +#     In this mode, all this script does is to run the next command:
> +#
> +#         vim -f -c ... $LOCAL $BASE $REMOTE $MERGED
> +#
> +#     ...where the "..." string depends on the value of the
> +#     "mergetool.vimdiff.layout" configuration variable and is used to open vim
> +#     with a certain layout of buffers, windows and tabs.
> +#
> +#   - From the shell, manually. This is only expected to be done by developers
> +#     who are editing this script. When this happens, the script runs a battery
> +#     of unit tests to make sure nothing breaks.
> +#     In this context this script does not expect any particular environment
> +#     variable to be set.
> +#
> +
> +
> +# Set to "true" to print debug messages to stderr
> +DEBUG=false
> +#DEBUG=true


It might be better to omit "DEBUG=false" and call it
GIT_MERGETOOL_VIMDIFF_DEBUG.


> +
> +
> +################################################################################
> +## Internal functions (not meant to be used outside this script)
> +################################################################################
> +
> +debug_print() {
> +       # Send message to stderr if global variable DEBUG is set to "true"
> +
> +       if test "$DEBUG" = "true"
> +       then
> +               >&2 echo "$@";
> +       fi


... and then in here we can just check:

    if test -n "$GIT_MERGETOOL_VIMDIFF_DEBUG"
    then
        ....
    fi

and we won't ever have to edit the script to activate the debug mode
because it'll get inherited from the environment.



> +}
> +
> +
> +gen_cmd_aux() {
> +       # Auxiliary function used from "gen_cmd()".
> +       # Read that other function documentation for more details.
> +
> +       local LAYOUT=$1
> +       local CMD=$2  # This is a second (hidden) argument used for recursion


I believe "local" and other features used in this script are bash-isms.

We have to stick to a strict portable posix shell subset, so there's
some stuff here that will need adjustment for maximal portability.

We can't use any bashisms and we avoid "local" in scripted porcelains.

We also avoid shell arrays and "Substring Expansion" ${parameter:offset:length}.

https://github.com/git/git/blob/master/Documentation/CodingGuidelines#L41

That's going to require some rework of the implementation below to avoid these.


> +
> +       debug_print
> +       debug_print "LAYOUT    : $LAYOUT"
> +       debug_print "CMD       : $CMD"
> +
> +       if test -z "$CMD"
> +       then
> +               CMD="echo" # vim "nop" operator
> +       fi
> +
> +       local start=0
> +       local end=${#LAYOUT}
> +
> +       local nested=0
> +       local nested_min=100
> +
> +
> +       # Step 1:
> +       #
> +       # Increase/decrease "start"/"end" indices respectively to get rid of
> +       # outer parenthesis.
> +       #
> +       # Example:
> +       #
> +       #   - BEFORE: (( LOCAL | BASE ) - MERGED )
> +       #   - AFTER :  ( LOCAL | BASE ) - MERGED
> +
> +       for (( i=$start; i<$end; i++ )); do

Please avoid multiple statements on a single line (the ";" should be a
line break instead).

The for loop is a bash-ism. An alternative might be...

for i in $(seq $start $end)
do
    ...
done

but that is off-by-one because "seq" includes the $end value, so it'll
need to be decremented by 1.



> +               if test "${LAYOUT:$i:1}" = " "
> +               then
> +                       continue
> +               fi

This is going to need rework because we can't use "${LAYOUT:$i:1}".


> +
> +               if test "${LAYOUT:$i:1}" = "("
> +               then
> +                       nested=$(( nested + 1 ))


I mentioned this in the documentation commit as a comment about splitting a
long line, but we do not use Process Substitution <(list) or >(list) either
so that's another reason to break up the pipeline I mentioned in the previous
email.


Arithmetic substitution is something we do use, though, so this would be fine.


> +                       continue
> +               fi
> +
> +               if test "${LAYOUT:$i:1}" = ")"
> +               then
> +                       nested=$(( nested - 1 ))
> +                       continue
> +               fi
> +
> +               if test "$nested" -lt "$nested_min"
> +               then
> +                       nested_min=$nested
> +               fi
> +       done
> +
> +       debug_print "NESTED MIN: $nested_min"
> +
> +       while test "$nested_min" -gt "0"
> +       do
> +               start=$(( start + 1 ))
> +               end=$(( end - 1 ))
> +
> +               start_minus_one=$(( start - 1 ))
> +
> +               while ! test "${LAYOUT:$start_minus_one:1}" = "("
> +               do
> +                       start=$(( start + 1 ))
> +                       start_minus_one=$(( start_minus_one + 1 ))
> +               done
> +
> +               while ! test "${LAYOUT:$end:1}" = ")"
> +               do
> +                       end=$(( end - 1 ))
> +               done
> +
> +               nested_min=$(( nested_min - 1 ))
> +       done
> +
> +        debug_print "CLEAN     : ${LAYOUT:$start:$(( end - start ))}"
> +
> +
> +       # Step 2:
> +       #
> +       # Search for all valid separators (";", "-" or "|") which are *not*
> +       # inside parenthesis. Save the index at which each of them makes the
> +       # first appearance.


I now understand why the parens are helpful. They seem to make the
implementation simpler/possible.

> +
> +       local index_semicolon=""
> +       local index_minus=""
> +       local index_pipe=""

Drop "local".

Semantic names for these might be helpful. "index_new_tab",
"index_vertical_split" and "index_horizontal_split" might be easier to
understand and would be resilient to sugs about using different
tokens.


> +
> +       nested=0
> +       for (( i=$start; i<$end; i++ )); do
> +               if test "${LAYOUT:$i:1}" = " "
> +               then
> +                       continue
> +               fi
> +
> +               if test "${LAYOUT:$i:1}" = "("
> +               then
> +                       nested=$(( nested + 1 ))


Here and below -- should that be nested=$(( $nested + 1 )) ?

It seems to be missing a '$' prefix on the inner $nested variable.

My shell accepts both but the predominant style in the git test suite
is the use $nested, so let's do that.



> +                       continue
> +               fi
> +
> +               if test "${LAYOUT:$i:1}" = ")"
> +               then
> +                       nested=$(( nested - 1 ))
> +                       continue
> +               fi
> +
> +               if test "$nested" -eq "0"
> +               then
> +                       current=${LAYOUT:$i:1}
> +
> +                       if test "$current" = ";"
> +                       then
> +                               if test -z "$index_semicolon"
> +                               then
> +                                       index_semicolon=$i
> +                               fi
> +
> +                       elif test "$current" = "-"
> +                       then
> +                               if test -z "$index_minus"
> +                               then
> +                                       index_minus=$i
> +                               fi
> +
> +                       elif test "$current" = "|"
> +                       then
> +                               if test -z "$index_pipe"
> +                               then
> +                                       index_pipe=$i
> +                               fi
> +                       fi
> +               fi
> +       done
> +
> +
> +       # Step 3:
> +       #
> +       # Process the separator with the highest order of precedence
> +       # (";" has the highest precedence and "|" the lowest one).
> +       #
> +       # By "process" I mean recursively call this function twice: the first
> +       # one with the substring at the left of the separator and the second one
> +       # with the one at its right.
> +
> +       local terminate="false"
> +
> +       if ! test -z "$index_semicolon"
> +       then
> +               before="-tabnew"
> +               after="tabnext"
> +               index=$index_semicolon
> +               terminate="true"
> +
> +       elif ! test -z "$index_minus"
> +       then
> +               before="split"
> +               after="wincmd j"
> +               index=$index_minus
> +               terminate="true"
> +
> +       elif ! test -z "$index_pipe"
> +       then
> +               before="vertical split"
> +               after="wincmd l"
> +               index=$index_pipe
> +               terminate="true"
> +       fi
> +
> +       if  test "$terminate" = "true"
> +       then
> +               CMD="$CMD | $before"
> +               CMD=$(gen_cmd_aux "${LAYOUT:$start:$(( index - start ))}" "$CMD")
> +               CMD="$CMD | $after"
> +               CMD=$(gen_cmd_aux "${LAYOUT:$(( index + 1 )):$(( ${#LAYOUT} - index ))}" "$CMD")
> +               echo $CMD
> +               return
> +       fi
> +
> +
> +       # Step 4:
> +       #
> +       # If we reach this point, it means there are no separators and we just
> +       # need to print the command to display the specified buffer
> +
> +       local target=$(echo "${LAYOUT:$start:$(( end - start ))}" | sed 's:[ *();|-]::g')
> +
> +       if test "$target" = "LOCAL"
> +       then
> +               CMD="$CMD | 1b"
> +
> +       elif test "$target" = "BASE"
> +       then
> +               CMD="$CMD | 2b"
> +
> +       elif test "$target" = "REMOTE"
> +       then
> +               CMD="$CMD | 3b"
> +
> +       elif test "$target" = "MERGED"
> +       then
> +               CMD="$CMD | 4b"
> +
> +       else
> +               CMD="$CMD | ERROR: >$target<"
> +       fi
> +
> +       echo $CMD
> +       return
> +}
> +
> +
> +gen_cmd() {
> +       # This function returns (in global variable FINAL_CMD) the string that
> +       # you can use when invoking "vim" (as shown next) to obtain a given
> +       # layout:
> +       #
> +       #   $ vim -f $FINAL_CMD "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
> +       #
> +       # It takes one single argument: a string containing the desired layout
> +       # definition.
> +       #
> +       # The syntax of the "layout definitions" is explained in ... (TODO)...
> +       # but you can already intuitively understand how it works by knowing
> +       # that...
> +       #
> +       #   * ";" means "a new vim tab"
> +       #   * "-" means "a new vim horizontal split"
> +       #   * "|" means "a new vim vertical split"
> +       #
> +       # It also returns (in global variable FINAL_TARGET) the name ("LOCAL",
> +       # "BASE", "REMOTE" or "MERGED") of the file that is marked with an "*",
> +       # or "MERGED" if none of them is.
> +       #
> +       # Example:
> +       #
> +       #     gen_cmd "LOCAL* | REMOTE"
> +       #     |
> +       #     `-> FINAL_CMD    == "-c \"echo | vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\""
> +       #         FINAL_TARGET == "LOCAL"
> +
> +       local LAYOUT=$1
> +
> +
> +       # Search for a "*" in one of the files identifiers ("LOCAL", "BASE",
> +       # "REMOTE", "MERGED"). If not found, use "MERGE" as the default file
> +       # where changes will be saved.
> +
> +       AUX=$(echo "$LAYOUT" | grep -oe "[A-Z]\+\*")


From Documentatin/CodingGuidelines:

As to use of grep, stick to a subset of BRE (namely, no {m,n},
   [::], [==], or [..]) for portability.



> +
> +       if ! test -z "$AUX"
> +       then
> +               FINAL_TARGET="${AUX:0:-1}"
> +       else
> +               FINAL_TARGET="MERGED"
> +       fi


The conditional above is better written as:

    if test -n "$AUX"
    then
        ...
    else
        ...
    fi

"test -n" is the logical opposite of "test -z", so "! test -z" can be
replaced with "test -n".




> +
> +
> +       # Obtain the first part of vim "-c" option to obtain the desired layout
> +
> +       CMD=$(gen_cmd_aux "$LAYOUT")
> +
> +
> +       # Adjust the just obtained script depending on whether more than one
> +       # windows are visible or not
> +
> +       if test $(echo $LAYOUT | wc -w) == "1"
> +       then
> +               CMD="$CMD | bufdo diffthis"
> +        else
> +               CMD="$CMD | tabdo windo diffthis"
> +       fi


The output of "wc -c" is non-portable. It contains leading whitespace
on some platforms.

The test expression should be:

   test "$value" = 1

with a single "=" rather than "==".



> +
> +
> +       # Add an extra "-c" option to move to the first tab (notice that we
> +       # can't simply append the command to the previous "-c" string as
> +       # explained here: https://github.com/vim/vim/issues/9076
> +
> +       FINAL_CMD="-c \"$CMD\" -c \"tabfirst\""
> +}
> +
> +
> +run_unit_tests() {
> +       # Function to make sure that we don't break anything when modifying this
> +       # script.
> +       #
> +       # This function is automatically executed when you execute this script
> +       # from the shell with environment variable "DEBUG_GIT_VIMDIFF" set (to
> +       # any value).
> +
> +       local test_cases=(
> +               `#Test case 00` "LOCAL | MERGED | REMOTE"
> +               `#Test case 01` "LOCAL - MERGED - REMOTE"
> +               `#Test case 02` "(LOCAL - REMOTE) | MERGED"
> +               `#Test case 03` "MERGED | (LOCAL - REMOTE)"
> +               `#Test case 04` "(LOCAL | REMOTE) - MERGED"
> +               `#Test case 05` "MERGED - (LOCAL | REMOTE)"
> +               `#Test case 06` "(LOCAL | BASE | REMOTE) - MERGED"
> +               `#Test case 07` "(LOCAL - BASE - REMOTE) | MERGED"
> +               `#Test case 08` "LOCAL* | REMOTE"
> +               `#Test case 09` "MERGED"
> +               `#Test case 10` "(LOCAL | BASE | REMOTE) - MERGED; BASE | LOCAL; BASE | REMOTE; (LOCAL - BASE - REMOTE) | MERGED"
> +               `#Test case 11` "((LOCAL | REMOTE) - BASE) | MERGED"
> +               `#Test case 12` "((LOCAL | REMOTE) - BASE) | ((LOCAL - REMOTE) | MERGED)"
> +               `#Test case 13` "BASE | REMOTE ; BASE | LOCAL"
> +       )
> +
> +       local expected_cmd=(
> +               `#Test case 00` "-c \"echo | vertical split | 1b | wincmd l | vertical split | 4b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 01` "-c \"echo | split | 1b | wincmd j | split | 4b | wincmd j | 3b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 02` "-c \"echo | vertical split | split | 1b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 03` "-c \"echo | vertical split | 4b | wincmd l | split | 1b | wincmd j | 3b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 04` "-c \"echo | split | vertical split | 1b | wincmd l | 3b | wincmd j | 4b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 05` "-c \"echo | split | 4b | wincmd j | vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 06` "-c \"echo | split | vertical split | 1b | wincmd l | vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 07` "-c \"echo | vertical split | split | 1b | wincmd j | split | 2b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 08` "-c \"echo | vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 09` "-c \"echo | 4b | bufdo diffthis\" -c \"tabfirst\""
> +               `#Test case 10` "-c \"echo | -tabnew | split | vertical split | 1b | wincmd l | vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnext | -tabnew | vertical split | 2b | wincmd l | 1b | tabnext | -tabnew | vertical split | 2b | wincmd l | 3b | tabnext | vertical split | split | 1b | wincmd j | split | 2b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 11` "-c \"echo | vertical split | split | vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 12` "-c \"echo | vertical split | split | vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | vertical split | split | 1b | wincmd j | 3b | wincmd l | 4b | tabdo windo diffthis\" -c \"tabfirst\""
> +               `#Test case 13` "-c \"echo | -tabnew | vertical split | 2b | wincmd l | 3b | tabnext | vertical split | 2b | wincmd l | 1b | tabdo windo diffthis\" -c \"tabfirst\""
> +       )
> +
> +       local expected_target=(
> +               `#Test case 00` "MERGED"
> +               `#Test case 01` "MERGED"
> +               `#Test case 02` "MERGED"
> +               `#Test case 03` "MERGED"
> +               `#Test case 04` "MERGED"
> +               `#Test case 05` "MERGED"
> +               `#Test case 06` "MERGED"
> +               `#Test case 07` "MERGED"
> +               `#Test case 08` "LOCAL"
> +               `#Test case 09` "MERGED"
> +               `#Test case 10` "MERGED"
> +               `#Test case 11` "MERGED"
> +               `#Test case 12` "MERGED"
> +               `#Test case 13` "MERGED"
> +       )


We can't use shell arrays. This part really tells me that I don't
understand bash at all.

I don't understand what the backticks are doing.. is it actually
running something?

That's just a rhetorical question.. I don't actually know, but it's okay if we
ignore this question since we already indicated that we'll have to do
w/out arrays.




> +
> +       local at_least_one_ko="false"
> +
> +       for i in ${!test_cases[@]}; do


I suspect ${!test_cases[@]} is some bash-ism that we can't use.

For the final patch, I think it would be good if we had a way to run this
through the test suite if possible rather than needing to run the script
directly.

It might have more leeway to setup the environment for testing that way too.


> +               gen_cmd "${test_cases[$i]}"
> +
> +               if test "$FINAL_CMD" = "${expected_cmd[$i]}" && test "$FINAL_TARGET" = "${expected_target[$i]}"
> +               then
> +                       printf "Test Case #%02d: OK\n" $i
> +               else
> +                       printf "Test Case #%02d: KO !!!!\n" $i
> +                       echo "  FINAL_CMD              : $FINAL_CMD"
> +                        echo "  FINAL_CMD (expected)   : ${expected_cmd[$i]}"
> +                       echo "  FINAL_TARGET           : $FINAL_TARGET"
> +                        echo "  FINAL_TARGET (expected): ${expected_target[$i]}"
> +                       at_least_one_ko="true"
> +               fi
> +       done
> +
> +       if test "$at_least_one_ko" = "true"
> +       then
> +               return -1
> +       else
> +               return 0
> +       fi
> +}
> +
> +
> +################################################################################
> +## API functions (called from "git-mergetool--lib.sh")
> +################################################################################
> +
>  diff_cmd () {
>         "$merge_tool_path" -R -f -d \
>                 -c 'wincmd l' -c 'cd $GIT_PREFIX' "$LOCAL" "$REMOTE"
>  }
>
> +
>  merge_cmd () {
> +       layout=$(git config mergetool.$merge_tool.layout)
> +       print_warning="false"
> +
>         case "$1" in
>         *vimdiff)
> -               if $base_present
> +               if test -z "$layout"
>                 then
> -                       "$merge_tool_path" -f -d -c '4wincmd w | wincmd J' \
> -                               "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
> -               else
> -                       "$merge_tool_path" -f -d -c 'wincmd l' \
> -                               "$LOCAL" "$MERGED" "$REMOTE"
> +                       # Default layout when none is specified
> +                       layout="(LOCAL | BASE | REMOTE) - MERGED"
>                 fi
>                 ;;
>         *vimdiff1)
> -               "$merge_tool_path" -f -d \
> -                       -c 'echon "Resolve conflicts leftward then save. Use :cq to abort."' \
> -                       "$LOCAL" "$REMOTE"
> -               ret="$?"
> -               if test "$ret" -eq 0
> -               then
> -                       cp -- "$LOCAL" "$MERGED"
> -               fi
> -               return "$ret"
> +               layout="LOCAL* | MERGED"
> +               print_warning="true"
>                 ;;
>         *vimdiff2)
> -               "$merge_tool_path" -f -d -c 'wincmd l' \
> -                       "$LOCAL" "$MERGED" "$REMOTE"
> +               layout="LOCAL | MERGED | REMOTE"
> +               print_warning="true"
>                 ;;
>         *vimdiff3)
> -               if $base_present
> -               then
> -                       "$merge_tool_path" -f -d -c 'hid | hid | hid' \
> -                               "$LOCAL" "$REMOTE" "$BASE" "$MERGED"
> -               else
> -                       "$merge_tool_path" -f -d -c 'hid | hid' \
> -                               "$LOCAL" "$REMOTE" "$MERGED"
> -               fi
> +               layout="MERGED"
> +               print_warning="true"
>                 ;;
>         esac
> +
> +       if test "$print_warning" = "true"
> +       then
> +               echo "WARNING:"
> +               echo "WARNING: '$1' is going to be removed in a future version. You will be"
> +               echo "WARNING: able to obtain the same result by selecting 'vimdiff' as the merge"
> +               echo "WARNING: tool and setting configuration variable 'mergetool.vimdiff.layout'"
> +               echo "WARNING: to the following value:"
> +               echo "WARNING:"
> +               echo "WARNING:     layout = \"$layout\""
> +               echo "WARNING:"
> +               echo "Press ENTER to continue..."
> +               read
> +       fi

I wonder if we should ever remove the old variants.

We could just keep them around forever, and then users won't ever be bothered.

The bulk of the improvement here is to improve the implementation so
that we don't
ever have to add any new variants, and to fold the implementations
together into a
common approach so that there's less code to maintain.

It seems like there's really no need to burden users with our
implementation choices.

I personally would be in favor of dropping the deprecation angle and treating
these patches more-so as an refactoring of the implementation.


> +
> +       gen_cmd "$layout"
> +
> +       debug_print ""
> +       debug_print "FINAL CMD : $FINAL_CMD"
> +       debug_print "FINAL TAR : $FINAL_TARGET"
> +
> +       if $base_present
> +       then
> +               eval "$merge_tool_path" \
> +                       -f $FINAL_CMD "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
> +       else
> +               # If there is no BASE (example: a merge conflict in a new file
> +               # with the same name created in both braches which didn't exist
> +               # before), close all BASE windows using vim's "quit" command
> +
> +               FINAL_CMD=$(echo $FINAL_CMD | \
> +                       sed -e 's:2b:quit:g' -e 's:3b:2b:g' -e 's:4b:3b:g')
> +
> +               eval "$merge_tool_path" \
> +                       -f $FINAL_CMD "$LOCAL" "$REMOTE" "$MERGED"
> +       fi
> +
> +
> +       ret="$?"
> +       if test "$ret" -eq 0

This should be:

    if test "$ret" = 0

> +       then
> +               if test "$FINAL_TARGET" != "MERGED"
> +               then
> +                       eval cp -- \$"$FINAL_TARGET" "$MERGED"

This eval may not be safe when the value contains whitespace or shell
metacharacters.

I think it might be better to just spell it out and be explicit.

It's more code but it'll be easier to follow:

case "$FINAL_TARGET" in
LOCAL)
    source_path="$LOCAL"
    ;;
REMOTE)
    source_path="$REMOTE"
    ;;
MERGED|*)
    # Do nothing
    source_path=
    ;;
esac

if test -n "$source_path"
then
    cp -- "$source_path" "$MERGED"
fi



> +               fi
> +       fi
> +       return "$ret"
>  }
>
> +
>  translate_merge_tool_path() {
>         case "$1" in
>         nvimdiff*)
> @@ -57,14 +518,31 @@ translate_merge_tool_path() {
>         esac
>  }
>
> +
>  exit_code_trustable () {
>         true
>  }
>
> +
>  list_tool_variants () {
>         for prefix in '' g n; do
> -               for suffix in '' 1 2 3; do
> +               for suffix in '' 1 2 3
> +               do
>                         echo "${prefix}vimdiff${suffix}"
>                 done
>         done
>  }
> +
> +
> +################################################################################
> +## Run unit tests when calling this script from a shell
> +################################################################################
> +
> +if test $(ps -o stat= -p $PPID) = "Ss" && test $(ps -o stat= -p $$) = "S+"
> +then
> +       # Script is being manually run from command line (see
> +       # https://stackoverflow.com/questions/4261876/check-if-bash-script-was-invoked-from-a-shell-or-another-script-application)
> +
> +       run_unit_tests
> +fi


I'm not 100% sure, but I suspect this is probably non-portable and
will have some issues.

This is another vote for setting something up to go through the test suite.

The test suite can set some GIT_MERGETOOL_VIMDIFF_TESTING variable
that the scriplet
can key off of explicitly rather than having the script embed this stuff in it.

A test could then source the scriptlet directly and exercise functions
provided by it.

If the implementation details get too complex for shell, I wonder if
it's time to split
off a small helper command in C that does the layout -> vim script
translation. TBD.

The parsing code is pretty complex but if there's a way to do it in our portable
posix shell subset then we'll take that. There might be a point where having a
small dedicated helper command might be easier.

You'll probably have the best sense of that, though.

Thanks for diving into making this happen.
-- 
David

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] vimdiff: new implementation with layout support
  2021-11-07 22:41   ` David Aguilar
@ 2021-11-08  0:47     ` Eric Sunshine
  0 siblings, 0 replies; 9+ messages in thread
From: Eric Sunshine @ 2021-11-08  0:47 UTC (permalink / raw)
  To: David Aguilar
  Cc: Fernando Ramos, Git Mailing List, Junio C Hamano, Seth House,
	Philippe Blain, rogi

On Sun, Nov 7, 2021 at 5:42 PM David Aguilar <davvid@gmail.com> wrote:
> On Thu, Nov 4, 2021 at 9:10 AM Fernando Ramos <greenfoo@u92.eu> wrote:
> > +       AUX=$(echo "$LAYOUT" | grep -oe "[A-Z]\+\*")
>
> From Documentatin/CodingGuidelines:
>
> As to use of grep, stick to a subset of BRE (namely, no {m,n},
>    [::], [==], or [..]) for portability.

Also, `grep -o` isn't POSIX.

> > +       if test $(echo $LAYOUT | wc -w) == "1"
> > +       then
> > +               CMD="$CMD | bufdo diffthis"
> > +        else
> > +               CMD="$CMD | tabdo windo diffthis"
> > +       fi
>
> The output of "wc -c" is non-portable. It contains leading whitespace
> on some platforms.
>
> The test expression should be:
>
>    test "$value" = 1
>
> with a single "=" rather than "==".

For clarification, the leading whitespace emitted by some `wc`
implementations is only a problem when encapsulated in a string. For
instance, like this:

    if test "$(... | wc -w)" = "1"

in which case "  1"  won't equal "1". The usage here, however, should
be okay since the output is not quoted.

Quite correct about using "=" (or even "-eq") here rather than "==", though.

> > +       if $base_present
> > +       then
> > +               eval "$merge_tool_path" \
> > +                       -f $FINAL_CMD "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
> > +       else
> > +               [...]
> > +               eval "$merge_tool_path" \
> > +                       -f $FINAL_CMD "$LOCAL" "$REMOTE" "$MERGED"
> > +       fi
> > +
> > +       ret="$?"
> > +       if test "$ret" -eq 0
>
> This should be:
>
>     if test "$ret" = 0

Or simpler, no need for `ret` at all:

    if test $? -eq 0

(or `if test $? = 0` -- either works)

Another (perhaps better) alternative is to assign the result of `eval`
to `ret` at the point of invocation, which lessens the cognitive load
a bit since you don't have to scan backward through the code trying to
figure out what $? refers to.

Also, why is `eval` needed here? Is there something non-obvious going
on? (Genuine question; I didn't trace the code thoroughly to
understand.)

> > +                       eval cp -- \$"$FINAL_TARGET" "$MERGED"
>
> This eval may not be safe when the value contains whitespace or shell
> metacharacters.
>
> I think it might be better to just spell it out and be explicit.
>
> It's more code but it'll be easier to follow:
> [...]
> if test -n "$source_path"
> then
>     cp -- "$source_path" "$MERGED"
> fi

I suspect `--` also needs to be avoided since it is not POSIX.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/3] vimdiff: add tool documentation
  2021-11-07 21:24   ` David Aguilar
@ 2021-11-08  1:02     ` Eric Sunshine
  2021-11-08 19:08     ` Junio C Hamano
  1 sibling, 0 replies; 9+ messages in thread
From: Eric Sunshine @ 2021-11-08  1:02 UTC (permalink / raw)
  To: David Aguilar
  Cc: Fernando Ramos, Git Mailing List, Junio C Hamano, Seth House,
	Philippe Blain, rogi

On Sun, Nov 7, 2021 at 4:25 PM David Aguilar <davvid@gmail.com> wrote:
> On Thu, Nov 4, 2021 at 9:10 AM Fernando Ramos <greenfoo@u92.eu> wrote:
> > +                               while IFS= read -r line
> > +                               do
> > +                                       printf "%s\t%s\n" "$per_line_prefix" "$line"
> > +                                done < <(diff_mode && diff_cmd_help "$toolname" || merge_cmd_help "$toolname")
>
> If we wanted to shorten this line a bit, would it work to run the pipeline
> first and pipe the result?
>
>     (diff_mode && ... || merge_cmd_help ...) |
>     while IFS= read -r line
>     do
>        ...
>     done

The additional benefit is that this avoids the `<(...)` Bashism (which
you mentioned in your other review).

> > +               cat <<-ENDOFMESSAGE
> > +                       Opens vim with two vertical windows: LOCAL changes will be placed in the left
> > +                       window and REMOTE changes in the right one.
> > +               ENDOFMESSAGE
>
> Tiny nit: we call this EOF in the other places
> (git-mergetool--helper.sh) where we do the same.
>
> ENDOFMESSAGE is a bit verbose so it might be worth sticking to the
> conventional EOF marker.

A couple additional really micro nits (take them or leave them)...

We normally don't add extra indentation to the here-doc body, and we
use `<<-\EOF` to reduce cognitive load if there's nothing in the body
which requires interpolation or expansion. Thus:

    cat <<-\EOF
    Opens vim with two...
    EOF

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/3] vimdiff: add tool documentation
  2021-11-07 21:24   ` David Aguilar
  2021-11-08  1:02     ` Eric Sunshine
@ 2021-11-08 19:08     ` Junio C Hamano
  1 sibling, 0 replies; 9+ messages in thread
From: Junio C Hamano @ 2021-11-08 19:08 UTC (permalink / raw)
  To: David Aguilar
  Cc: Fernando Ramos, Git Mailing List, Seth House, levraiphilippeblain,
	rogi

David Aguilar <davvid@gmail.com> writes:

>     (diff_mode && ... || merge_cmd_help ...) |
>     while IFS= read -r line
>     do
>        ...
>     done
>
> How come we have to unset IFS here?

We are taking the input into a single variable "line", so what are
we splitting with IFS anyway?

In any case, thanks for spotting bash-isms to avoid.  We started
allowing "local", I think, at least in the tests, but otherwise many
bash-only things like "<(process)" redirection and "${substring:4}"
substitution are simply no-no.



^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-11-08 19:09 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-04 16:09 [PATCH 0/3] vimdiff: new layout option + docs Fernando Ramos
2021-11-04 16:09 ` [PATCH 1/3] vimdiff: new implementation with layout support Fernando Ramos
2021-11-07 22:41   ` David Aguilar
2021-11-08  0:47     ` Eric Sunshine
2021-11-04 16:09 ` [PATCH 2/3] vimdiff: add tool documentation Fernando Ramos
2021-11-07 21:24   ` David Aguilar
2021-11-08  1:02     ` Eric Sunshine
2021-11-08 19:08     ` Junio C Hamano
2021-11-04 16:09 ` [PATCH 3/3] vimdiff: remove deprecated {,g,n}vimdiff{1,2,3} variants Fernando Ramos

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).