From c8bae7493d2f2910b57f13ded012e86bdcfb0532 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 16:47:53 +0200 Subject: Adding upstream version 1:2.39.2. Signed-off-by: Daniel Baumann --- mergetools/araxis | 26 ++ mergetools/bc | 37 +++ mergetools/codecompare | 31 +++ mergetools/deltawalker | 33 +++ mergetools/diffmerge | 26 ++ mergetools/diffuse | 23 ++ mergetools/ecmerge | 22 ++ mergetools/emerge | 34 +++ mergetools/examdiff | 24 ++ mergetools/guiffy | 26 ++ mergetools/gvimdiff | 1 + mergetools/kdiff3 | 44 ++++ mergetools/kompare | 19 ++ mergetools/meld | 97 +++++++ mergetools/nvimdiff | 1 + mergetools/opendiff | 22 ++ mergetools/p4merge | 44 ++++ mergetools/smerge | 20 ++ mergetools/tkdiff | 24 ++ mergetools/tortoisemerge | 40 +++ mergetools/vimdiff | 638 +++++++++++++++++++++++++++++++++++++++++++++++ mergetools/winmerge | 23 ++ mergetools/xxdiff | 38 +++ 23 files changed, 1293 insertions(+) create mode 100644 mergetools/araxis create mode 100644 mergetools/bc create mode 100644 mergetools/codecompare create mode 100644 mergetools/deltawalker create mode 100644 mergetools/diffmerge create mode 100644 mergetools/diffuse create mode 100644 mergetools/ecmerge create mode 100644 mergetools/emerge create mode 100644 mergetools/examdiff create mode 100644 mergetools/guiffy create mode 100644 mergetools/gvimdiff create mode 100644 mergetools/kdiff3 create mode 100644 mergetools/kompare create mode 100644 mergetools/meld create mode 100644 mergetools/nvimdiff create mode 100644 mergetools/opendiff create mode 100644 mergetools/p4merge create mode 100644 mergetools/smerge create mode 100644 mergetools/tkdiff create mode 100644 mergetools/tortoisemerge create mode 100644 mergetools/vimdiff create mode 100644 mergetools/winmerge create mode 100644 mergetools/xxdiff (limited to 'mergetools') diff --git a/mergetools/araxis b/mergetools/araxis new file mode 100644 index 0000000..eb32a7d --- /dev/null +++ b/mergetools/araxis @@ -0,0 +1,26 @@ +diff_cmd () { + "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" >/dev/null 2>&1 +} + +diff_cmd_help () { + echo "Use Araxis Merge (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" -wait -merge -3 -a1 \ + "$BASE" "$LOCAL" "$REMOTE" "$MERGED" >/dev/null 2>&1 + else + "$merge_tool_path" -wait -2 \ + "$LOCAL" "$REMOTE" "$MERGED" >/dev/null 2>&1 + fi +} + +merge_cmd_help () { + echo "Use Araxis Merge (requires a graphical session)" +} + +translate_merge_tool_path() { + echo compare +} diff --git a/mergetools/bc b/mergetools/bc new file mode 100644 index 0000000..2922667 --- /dev/null +++ b/mergetools/bc @@ -0,0 +1,37 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" +} + +diff_cmd_help () { + echo "Use Beyond Compare (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" \ + -mergeoutput="$MERGED" + else + "$merge_tool_path" "$LOCAL" "$REMOTE" \ + -mergeoutput="$MERGED" + fi +} + +merge_cmd_help () { + echo "Use Beyond Compare (requires a graphical session)" +} + +translate_merge_tool_path() { + if type bcomp >/dev/null 2>/dev/null + then + echo bcomp + else + echo bcompare + fi +} + +list_tool_variants () { + echo bc + echo bc3 + echo bc4 +} diff --git a/mergetools/codecompare b/mergetools/codecompare new file mode 100644 index 0000000..610963d --- /dev/null +++ b/mergetools/codecompare @@ -0,0 +1,31 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" +} + +diff_cmd_help () { + echo "Use Code Compare (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" -MF="$LOCAL" -TF="$REMOTE" -BF="$BASE" \ + -RF="$MERGED" + else + "$merge_tool_path" -MF="$LOCAL" -TF="$REMOTE" \ + -RF="$MERGED" + fi +} + +merge_cmd_help () { + echo "Use Code Compare (requires a graphical session)" +} + +translate_merge_tool_path() { + if merge_mode + then + echo CodeMerge + else + echo CodeCompare + fi +} diff --git a/mergetools/deltawalker b/mergetools/deltawalker new file mode 100644 index 0000000..efae4c2 --- /dev/null +++ b/mergetools/deltawalker @@ -0,0 +1,33 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" >/dev/null 2>&1 +} + +diff_cmd_help () { + echo "Use DeltaWalker (requires a graphical session)" +} + +merge_cmd () { + # Adding $(pwd)/ in front of $MERGED should not be necessary. + # However without it, DeltaWalker (at least v1.9.8 on Windows) + # crashes with a JRE exception. The DeltaWalker user manual, + # shows $(pwd)/ whenever the '-merged' options is given. + # Adding it here seems to work around the problem. + if $base_present + then + "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" -merged="$(pwd)/$MERGED" + else + "$merge_tool_path" "$LOCAL" "$REMOTE" -merged="$(pwd)/$MERGED" + fi >/dev/null 2>&1 +} + +merge_cmd_help () { + echo "Use DeltaWalker (requires a graphical session)" +} + +translate_merge_tool_path () { + echo DeltaWalker +} + +exit_code_trustable () { + true +} diff --git a/mergetools/diffmerge b/mergetools/diffmerge new file mode 100644 index 0000000..9b5b62d --- /dev/null +++ b/mergetools/diffmerge @@ -0,0 +1,26 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" >/dev/null 2>&1 +} + +diff_cmd_help () { + echo "Use DiffMerge (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" --merge --result="$MERGED" \ + "$LOCAL" "$BASE" "$REMOTE" + else + "$merge_tool_path" --merge \ + --result="$MERGED" "$LOCAL" "$REMOTE" + fi +} + +merge_cmd_help () { + echo "Use DiffMerge (requires a graphical session)" +} + +exit_code_trustable () { + true +} diff --git a/mergetools/diffuse b/mergetools/diffuse new file mode 100644 index 0000000..ebfaba5 --- /dev/null +++ b/mergetools/diffuse @@ -0,0 +1,23 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" | cat +} + +diff_cmd_help () { + echo "Use Diffuse (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" \ + "$LOCAL" "$MERGED" "$REMOTE" \ + "$BASE" | cat + else + "$merge_tool_path" \ + "$LOCAL" "$MERGED" "$REMOTE" | cat + fi +} + +merge_cmd_help () { + echo "Use Diffuse (requires a graphical session)" +} diff --git a/mergetools/ecmerge b/mergetools/ecmerge new file mode 100644 index 0000000..0d4d609 --- /dev/null +++ b/mergetools/ecmerge @@ -0,0 +1,22 @@ +diff_cmd () { + "$merge_tool_path" --default --mode=diff2 "$LOCAL" "$REMOTE" +} + +diff_cmd_help () { + echo "Use ECMerge (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \ + --default --mode=merge3 --to="$MERGED" + else + "$merge_tool_path" "$LOCAL" "$REMOTE" \ + --default --mode=merge2 --to="$MERGED" + fi +} + +merge_cmd_help () { + echo "Use ECMerge (requires a graphical session)" +} diff --git a/mergetools/emerge b/mergetools/emerge new file mode 100644 index 0000000..fc6892c --- /dev/null +++ b/mergetools/emerge @@ -0,0 +1,34 @@ +diff_cmd () { + "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" +} + +diff_cmd_help () { + echo "Use Emacs' Emerge" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" \ + -f emerge-files-with-ancestor-command \ + "$LOCAL" "$REMOTE" "$BASE" \ + "$(basename "$MERGED")" + else + "$merge_tool_path" \ + -f emerge-files-command \ + "$LOCAL" "$REMOTE" \ + "$(basename "$MERGED")" + fi +} + +merge_cmd_help () { + echo "Use Emacs' Emerge" +} + +translate_merge_tool_path() { + echo emacs +} + +exit_code_trustable () { + true +} diff --git a/mergetools/examdiff b/mergetools/examdiff new file mode 100644 index 0000000..6f53ca9 --- /dev/null +++ b/mergetools/examdiff @@ -0,0 +1,24 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" -nh +} + +diff_cmd_help () { + echo "Use ExamDiff Pro (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" -merge "$LOCAL" "$BASE" "$REMOTE" -o:"$MERGED" -nh + else + "$merge_tool_path" -merge "$LOCAL" "$REMOTE" -o:"$MERGED" -nh + fi +} + +merge_cmd_help () { + echo "Use ExamDiff Pro (requires a graphical session)" +} + +translate_merge_tool_path() { + mergetool_find_win32_cmd "ExamDiff.com" "ExamDiff Pro" +} diff --git a/mergetools/guiffy b/mergetools/guiffy new file mode 100644 index 0000000..3ed07ef --- /dev/null +++ b/mergetools/guiffy @@ -0,0 +1,26 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" +} + +diff_cmd_help () { + echo "Use Guiffy's Diff Tool (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" -s "$LOCAL" \ + "$REMOTE" "$BASE" "$MERGED" + else + "$merge_tool_path" -m "$LOCAL" \ + "$REMOTE" "$MERGED" + fi +} + +merge_cmd_help () { + echo "Use Guiffy's Diff Tool (requires a graphical session)" +} + +exit_code_trustable () { + true +} diff --git a/mergetools/gvimdiff b/mergetools/gvimdiff new file mode 100644 index 0000000..04a5bb0 --- /dev/null +++ b/mergetools/gvimdiff @@ -0,0 +1 @@ +. "$MERGE_TOOLS_DIR/vimdiff" diff --git a/mergetools/kdiff3 b/mergetools/kdiff3 new file mode 100644 index 0000000..ee8b3a0 --- /dev/null +++ b/mergetools/kdiff3 @@ -0,0 +1,44 @@ +diff_cmd () { + "$merge_tool_path" \ + --L1 "$MERGED (A)" --L2 "$MERGED (B)" \ + "$LOCAL" "$REMOTE" >/dev/null 2>&1 +} + +diff_cmd_help () { + echo "Use KDiff3 (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" --auto \ + --L1 "$MERGED (Base)" \ + --L2 "$MERGED (Local)" \ + --L3 "$MERGED (Remote)" \ + -o "$MERGED" "$BASE" "$LOCAL" "$REMOTE" \ + >/dev/null 2>&1 + else + "$merge_tool_path" --auto \ + --L1 "$MERGED (Local)" \ + --L2 "$MERGED (Remote)" \ + -o "$MERGED" "$LOCAL" "$REMOTE" \ + >/dev/null 2>&1 + fi +} + +merge_cmd_help () { + echo "Use KDiff3 (requires a graphical session)" +} + +exit_code_trustable () { + true +} + +translate_merge_tool_path() { + if type kdiff3 >/dev/null 2>/dev/null + then + echo kdiff3 + else + mergetool_find_win32_cmd "kdiff3.exe" "Kdiff3" + fi +} diff --git a/mergetools/kompare b/mergetools/kompare new file mode 100644 index 0000000..4ce23db --- /dev/null +++ b/mergetools/kompare @@ -0,0 +1,19 @@ +can_merge () { + return 1 +} + +diff_cmd_help () { + echo "Use Kompare (requires a graphical session)" +} + +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" +} + +merge_cmd_help () { + echo "Use Kompare (requires a graphical session)" +} + +exit_code_trustable () { + true +} diff --git a/mergetools/meld b/mergetools/meld new file mode 100644 index 0000000..8ec0867 --- /dev/null +++ b/mergetools/meld @@ -0,0 +1,97 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" +} + +diff_cmd_help () { + echo "Use Meld (requires a graphical session)" +} + +merge_cmd () { + check_meld_for_features + + option_auto_merge= + if test "$meld_use_auto_merge_option" = true + then + option_auto_merge="--auto-merge" + fi + + if test "$meld_has_output_option" = true + then + "$merge_tool_path" $option_auto_merge --output="$MERGED" \ + "$LOCAL" "$BASE" "$REMOTE" + else + "$merge_tool_path" $option_auto_merge "$LOCAL" "$MERGED" "$REMOTE" + fi +} + +merge_cmd_help () { + echo "Use Meld (requires a graphical session) with optional \`auto merge\` (see \`git help mergetool\`'s \`CONFIGURATION\` section)" +} + +# Get meld help message +init_meld_help_msg () { + if test -z "$meld_help_msg" + then + meld_path="$(git config mergetool.meld.path || echo meld)" + meld_help_msg=$("$meld_path" --help 2>&1) + fi +} + +# Check the features and set flags +check_meld_for_features () { + # Check whether we should use 'meld --output ' + if test -z "$meld_has_output_option" + then + meld_has_output_option=$(git config --bool mergetool.meld.hasOutput) + case "$meld_has_output_option" in + true | false) + : use configured value + ;; + *) + : empty or invalid configured value, detecting "--output" automatically + init_meld_help_msg + + case "$meld_help_msg" in + *"--output="* | *'[OPTION...]'*) + # All version that has [OPTION...] supports --output + meld_has_output_option=true + ;; + *) + meld_has_output_option=false + ;; + esac + ;; + esac + fi + # Check whether we should use 'meld --auto-merge ...' + if test -z "$meld_use_auto_merge_option" + then + meld_use_auto_merge_option=$( + git config --bool-or-str mergetool.meld.useAutoMerge + ) + case "$meld_use_auto_merge_option" in + true | false) + : use well formatted boolean value + ;; + auto) + # testing the "--auto-merge" option only if config is "auto" + init_meld_help_msg + + case "$meld_help_msg" in + *"--auto-merge"* | *'[OPTION...]'*) + meld_use_auto_merge_option=true + ;; + *) + meld_use_auto_merge_option=false + ;; + esac + ;; + "") + meld_use_auto_merge_option=false + ;; + *) + die "unknown mergetool.meld.useAutoMerge: $meld_use_auto_merge_option" + ;; + esac + fi +} diff --git a/mergetools/nvimdiff b/mergetools/nvimdiff new file mode 100644 index 0000000..04a5bb0 --- /dev/null +++ b/mergetools/nvimdiff @@ -0,0 +1 @@ +. "$MERGE_TOOLS_DIR/vimdiff" diff --git a/mergetools/opendiff b/mergetools/opendiff new file mode 100644 index 0000000..44adf8f --- /dev/null +++ b/mergetools/opendiff @@ -0,0 +1,22 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" | cat +} + +diff_cmd_help () { + echo "Use FileMerge (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" "$LOCAL" "$REMOTE" \ + -ancestor "$BASE" -merge "$MERGED" | cat + else + "$merge_tool_path" "$LOCAL" "$REMOTE" \ + -merge "$MERGED" | cat + fi +} + +merge_cmd_help () { + echo "Use FileMerge (requires a graphical session)" +} diff --git a/mergetools/p4merge b/mergetools/p4merge new file mode 100644 index 0000000..f3cb197 --- /dev/null +++ b/mergetools/p4merge @@ -0,0 +1,44 @@ +diff_cmd () { + empty_file= + + # p4merge does not like /dev/null + if test "/dev/null" = "$LOCAL" + then + LOCAL="$(create_empty_file)" + fi + if test "/dev/null" = "$REMOTE" + then + REMOTE="$(create_empty_file)" + fi + + "$merge_tool_path" "$LOCAL" "$REMOTE" + + if test -n "$empty_file" + then + rm -f "$empty_file" + fi +} + +diff_cmd_help () { + echo "Use HelixCore P4Merge (requires a graphical session)" +} + +merge_cmd () { + if ! $base_present + then + cp -- "$LOCAL" "$BASE" + create_virtual_base "$BASE" "$REMOTE" + fi + "$merge_tool_path" "$BASE" "$REMOTE" "$LOCAL" "$MERGED" +} + +create_empty_file () { + empty_file="${TMPDIR:-/tmp}/git-difftool-p4merge-empty-file.$$" + >"$empty_file" + + printf "%s" "$empty_file" +} + +merge_cmd_help () { + echo "Use HelixCore P4Merge (requires a graphical session)" +} diff --git a/mergetools/smerge b/mergetools/smerge new file mode 100644 index 0000000..5410835 --- /dev/null +++ b/mergetools/smerge @@ -0,0 +1,20 @@ +diff_cmd () { + "$merge_tool_path" mergetool "$LOCAL" "$REMOTE" -o "$MERGED" +} + +diff_cmd_help () { + echo "Use Sublime Merge (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" mergetool "$BASE" "$LOCAL" "$REMOTE" -o "$MERGED" + else + "$merge_tool_path" mergetool "$LOCAL" "$REMOTE" -o "$MERGED" + fi +} + +merge_cmd_help () { + echo "Use Sublime Merge (requires a graphical session)" +} diff --git a/mergetools/tkdiff b/mergetools/tkdiff new file mode 100644 index 0000000..66906a7 --- /dev/null +++ b/mergetools/tkdiff @@ -0,0 +1,24 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" +} + +diff_cmd_help () { + echo "Use TkDiff (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE" + else + "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" + fi +} + +exit_code_trustable () { + true +} + +merge_cmd_help () { + echo "Use TkDiff (requires a graphical session)" +} diff --git a/mergetools/tortoisemerge b/mergetools/tortoisemerge new file mode 100644 index 0000000..507edcd --- /dev/null +++ b/mergetools/tortoisemerge @@ -0,0 +1,40 @@ +can_diff () { + return 1 +} + +diff_cmd_help () { + echo "Use TortoiseMerge (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + basename="$(basename "$merge_tool_path" .exe)" + if test "$basename" = "tortoisegitmerge" + then + "$merge_tool_path" \ + -base "$BASE" -mine "$LOCAL" \ + -theirs "$REMOTE" -merged "$MERGED" + else + "$merge_tool_path" \ + -base:"$BASE" -mine:"$LOCAL" \ + -theirs:"$REMOTE" -merged:"$MERGED" + fi + else + echo "$merge_tool_path cannot be used without a base" 1>&2 + return 1 + fi +} + +translate_merge_tool_path() { + if type tortoisegitmerge >/dev/null 2>/dev/null + then + echo tortoisegitmerge + else + echo tortoisemerge + fi +} + +merge_cmd_help () { + echo "Use TortoiseMerge (requires a graphical session)" +} diff --git a/mergetools/vimdiff b/mergetools/vimdiff new file mode 100644 index 0000000..06937ac --- /dev/null +++ b/mergetools/vimdiff @@ -0,0 +1,638 @@ +# 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 a script inside the unit tests framework folder ("t" folder) by +# sourcing this script and then manually calling "run_unit_tests", which +# will run 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. + + +################################################################################ +## Internal functions (not meant to be used outside this script) +################################################################################ + +debug_print () { + # Send message to stderr if global variable GIT_MERGETOOL_VIMDIFF_DEBUG + # is set. + + if test -n "$GIT_MERGETOOL_VIMDIFF_DEBUG" + then + >&2 echo "$@" + fi +} + +substring () { + # Return a substring of $1 containing $3 characters starting at + # zero-based offset $2. + # + # Examples: + # + # substring "Hello world" 0 4 --> "Hell" + # substring "Hello world" 3 4 --> "lo w" + # substring "Hello world" 3 10 --> "lo world" + + STRING=$1 + START=$2 + LEN=$3 + + echo "$STRING" | cut -c$(( START + 1 ))-$(( START + $LEN )) +} + +gen_cmd_aux () { + # Auxiliary function used from "gen_cmd()". + # Read that other function documentation for more details. + + LAYOUT=$1 + CMD=$2 # This is a second (hidden) argument used for recursion + + debug_print + debug_print "LAYOUT : $LAYOUT" + debug_print "CMD : $CMD" + + start=0 + end=${#LAYOUT} + + nested=0 + 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 + + oldIFS=$IFS + IFS=# + for c in $(echo "$LAYOUT" | sed 's:.:&#:g') + do + if test "$c" = " " + then + continue + fi + + if test "$c" = "(" + then + nested=$(( nested + 1 )) + continue + fi + + if test "$c" = ")" + then + nested=$(( nested - 1 )) + continue + fi + + if test "$nested" -lt "$nested_min" + then + nested_min=$nested + fi + done + IFS=$oldIFS + + 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 "$(substring "$LAYOUT" "$start_minus_one" 1)" = "(" + do + start=$(( start + 1 )) + start_minus_one=$(( start_minus_one + 1 )) + done + + while ! test "$(substring "$LAYOUT" "$end" 1)" = ")" + do + end=$(( end - 1 )) + done + + nested_min=$(( nested_min - 1 )) + done + + debug_print "CLEAN : $(substring "$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. + + index_horizontal_split="" + index_vertical_split="" + + nested=0 + i=$(( start - 1 )) + + oldIFS=$IFS + IFS=# + for c in $(substring "$LAYOUT" "$start" "$(( end - start ))" | sed 's:.:&#:g'); + do + i=$(( i + 1 )) + + if test "$c" = " " + then + continue + fi + + if test "$c" = "(" + then + nested=$(( nested + 1 )) + continue + fi + + if test "$c" = ")" + then + nested=$(( nested - 1 )) + continue + fi + + if test "$nested" = 0 + then + current=$c + + if test "$current" = "/" + then + if test -z "$index_horizontal_split" + then + index_horizontal_split=$i + fi + + elif test "$current" = "," + then + if test -z "$index_vertical_split" + then + index_vertical_split=$i + fi + fi + fi + done + IFS=$oldIFS + + + # 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. + + terminate="false" + + if ! test -z "$index_horizontal_split" + then + before="leftabove split" + after="wincmd j" + index=$index_horizontal_split + terminate="true" + + elif ! test -z "$index_vertical_split" + then + before="leftabove vertical split" + after="wincmd l" + index=$index_vertical_split + terminate="true" + fi + + if test "$terminate" = "true" + then + CMD="$CMD | $before" + CMD=$(gen_cmd_aux "$(substring "$LAYOUT" "$start" "$(( index - start ))")" "$CMD") + CMD="$CMD | $after" + CMD=$(gen_cmd_aux "$(substring "$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 + + target=$(substring "$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 "Documentation/ + # mergetools/vimdiff.txt" 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 | leftabove vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\"" + # FINAL_TARGET == "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. + + if echo "$LAYOUT" | grep @LOCAL >/dev/null + then + FINAL_TARGET="LOCAL" + elif echo "$LAYOUT" | grep @BASE >/dev/null + then + FINAL_TARGET="BASE" + else + FINAL_TARGET="MERGED" + fi + + + # Obtain the first part of vim "-c" option to obtain the desired layout + + CMD= + oldIFS=$IFS + IFS=+ + for tab in $LAYOUT + do + if test -z "$CMD" + then + CMD="echo" # vim "nop" operator + else + CMD="$CMD | tabnew" + fi + + # If this is a single window diff with all the buffers + if ! echo "$tab" | grep ",\|/" >/dev/null + then + CMD="$CMD | silent execute 'bufdo diffthis'" + fi + + CMD=$(gen_cmd_aux "$tab" "$CMD") + done + IFS=$oldIFS + + CMD="$CMD | execute 'tabdo windo diffthis'" + + FINAL_CMD="-c \"set hidden diffopt-=hiddenoff | $CMD | tabfirst\"" +} + + +################################################################################ +## 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" +} + + +diff_cmd_help () { + TOOL=$1 + + case "$TOOL" in + nvimdiff*) + printf "Use Neovim" + ;; + gvimdiff*) + printf "Use gVim (requires a graphical session)" + ;; + vimdiff*) + printf "Use Vim" + ;; + esac + + return 0 +} + + +merge_cmd () { + layout=$(git config mergetool.vimdiff.layout) + + case "$1" in + *vimdiff) + if test -z "$layout" + then + # Default layout when none is specified + layout="(LOCAL,BASE,REMOTE)/MERGED" + fi + ;; + *vimdiff1) + layout="@LOCAL,REMOTE" + ;; + *vimdiff2) + layout="LOCAL,MERGED,REMOTE" + ;; + *vimdiff3) + layout="MERGED" + ;; + esac + + 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 + 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 + + return "$ret" +} + + +merge_cmd_help () { + TOOL=$1 + + case "$TOOL" in + nvimdiff*) + printf "Use Neovim " + ;; + gvimdiff*) + printf "Use gVim (requires a graphical session) " + ;; + vimdiff*) + printf "Use Vim " + ;; + esac + + case "$TOOL" in + *1) + echo "with a 2 panes layout (LOCAL and REMOTE)" + ;; + *2) + echo "with a 3 panes layout (LOCAL, MERGED and REMOTE)" + ;; + *3) + echo "where only the MERGED file is shown" + ;; + *) + echo "with a custom layout (see \`git help mergetool\`'s \`BACKEND SPECIFIC HINTS\` section)" + ;; + esac + + return 0 +} + + +translate_merge_tool_path () { + case "$1" in + nvimdiff*) + echo nvim + ;; + gvimdiff*) + echo gvim + ;; + vimdiff*) + echo vim + ;; + esac +} + + +exit_code_trustable () { + true +} + + +list_tool_variants () { + if test "$TOOL_MODE" = "diff" + then + for prefix in '' g n + do + echo "${prefix}vimdiff" + done + else + for prefix in '' g n + do + for suffix in '' 1 2 3 + do + echo "${prefix}vimdiff${suffix}" + done + done + fi +} + + +################################################################################ +## Unit tests (called from scripts inside the "t" folder) +################################################################################ + +run_unit_tests () { + # Function to make sure that we don't break anything when modifying this + # script. + + NUMBER_OF_TEST_CASES=16 + + TEST_CASE_01="(LOCAL,BASE,REMOTE)/MERGED" # default behaviour + TEST_CASE_02="@LOCAL,REMOTE" # when using vimdiff1 + TEST_CASE_03="LOCAL,MERGED,REMOTE" # when using vimdiff2 + TEST_CASE_04="MERGED" # when using vimdiff3 + TEST_CASE_05="LOCAL/MERGED/REMOTE" + TEST_CASE_06="(LOCAL/REMOTE),MERGED" + TEST_CASE_07="MERGED,(LOCAL/REMOTE)" + TEST_CASE_08="(LOCAL,REMOTE)/MERGED" + TEST_CASE_09="MERGED/(LOCAL,REMOTE)" + TEST_CASE_10="(LOCAL/BASE/REMOTE),MERGED" + TEST_CASE_11="(LOCAL,BASE,REMOTE)/MERGED+BASE,LOCAL+BASE,REMOTE+(LOCAL/BASE/REMOTE),MERGED" + TEST_CASE_12="((LOCAL,REMOTE)/BASE),MERGED" + TEST_CASE_13="((LOCAL,REMOTE)/BASE),((LOCAL/REMOTE),MERGED)" + TEST_CASE_14="BASE,REMOTE+BASE,LOCAL" + TEST_CASE_15=" (( (LOCAL , BASE , REMOTE) / MERGED)) +(BASE) , LOCAL+ BASE , REMOTE+ (((LOCAL / BASE / REMOTE)) , MERGED ) " + TEST_CASE_16="LOCAL,BASE,REMOTE / MERGED + BASE,LOCAL + BASE,REMOTE + (LOCAL / BASE / REMOTE),MERGED" + + EXPECTED_CMD_01="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_02="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_03="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 4b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_04="-c \"set hidden diffopt-=hiddenoff | echo | silent execute 'bufdo diffthis' | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_05="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | 1b | wincmd j | leftabove split | 4b | wincmd j | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_06="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | 1b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_07="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 4b | wincmd l | leftabove split | 1b | wincmd j | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_08="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_09="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | 4b | wincmd j | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_10="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_11="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_12="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_13="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | leftabove vertical split | leftabove split | 1b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_14="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_15="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + EXPECTED_CMD_16="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\"" + + EXPECTED_TARGET_01="MERGED" + EXPECTED_TARGET_02="LOCAL" + EXPECTED_TARGET_03="MERGED" + EXPECTED_TARGET_04="MERGED" + EXPECTED_TARGET_05="MERGED" + EXPECTED_TARGET_06="MERGED" + EXPECTED_TARGET_07="MERGED" + EXPECTED_TARGET_08="MERGED" + EXPECTED_TARGET_09="MERGED" + EXPECTED_TARGET_10="MERGED" + EXPECTED_TARGET_11="MERGED" + EXPECTED_TARGET_12="MERGED" + EXPECTED_TARGET_13="MERGED" + EXPECTED_TARGET_14="MERGED" + EXPECTED_TARGET_15="MERGED" + EXPECTED_TARGET_16="MERGED" + + at_least_one_ko="false" + + for i in $(seq -w 1 99) + do + if test "$i" -gt $NUMBER_OF_TEST_CASES + then + break + fi + + gen_cmd "$(eval echo \${TEST_CASE_"$i"})" + + if test "$FINAL_CMD" = "$(eval echo \${EXPECTED_CMD_"$i"})" \ + && test "$FINAL_TARGET" = "$(eval echo \${EXPECTED_TARGET_"$i"})" + then + printf "Test Case #%02d: OK\n" "$(echo "$i" | sed 's/^0*//')" + else + printf "Test Case #%02d: KO !!!!\n" "$(echo "$i" | sed 's/^0*//')" + echo " FINAL_CMD : $FINAL_CMD" + echo " FINAL_CMD (expected) : $(eval echo \${EXPECTED_CMD_"$i"})" + echo " FINAL_TARGET : $FINAL_TARGET" + echo " FINAL_TARGET (expected): $(eval echo \${EXPECTED_TARGET_"$i"})" + at_least_one_ko="true" + fi + done + + # verify that `merge_cmd` handles paths with spaces + record_parameters () { + >actual + for arg + do + echo "$arg" >>actual + done + } + + base_present=false + LOCAL='lo cal' + BASE='ba se' + REMOTE="' '" + MERGED='mer ged' + merge_tool_path=record_parameters + + merge_cmd vimdiff || at_least_one_ko=true + + cat >expect <<-\EOF + -f + -c + set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | quit | wincmd l | 2b | wincmd j | 3b | execute 'tabdo windo diffthis' | tabfirst + lo cal + ' ' + mer ged + EOF + + diff -u expect actual || at_least_one_ko=true + + if test "$at_least_one_ko" = "true" + then + return 255 + else + return 0 + fi +} diff --git a/mergetools/winmerge b/mergetools/winmerge new file mode 100644 index 0000000..36c72dd --- /dev/null +++ b/mergetools/winmerge @@ -0,0 +1,23 @@ +diff_cmd () { + "$merge_tool_path" -u -e "$LOCAL" "$REMOTE" + return 0 +} + +diff_cmd_help () { + echo "Use WinMerge (requires a graphical session)" +} + +merge_cmd () { + # mergetool.winmerge.trustExitCode is implicitly false. + # touch $BACKUP so that we can check_unchanged. + "$merge_tool_path" -u -e -dl Local -dr Remote \ + "$LOCAL" "$REMOTE" "$MERGED" +} + +translate_merge_tool_path() { + mergetool_find_win32_cmd "WinMergeU.exe" "WinMerge" +} + +merge_cmd_help () { + echo "Use WinMerge (requires a graphical session)" +} diff --git a/mergetools/xxdiff b/mergetools/xxdiff new file mode 100644 index 0000000..cd205f9 --- /dev/null +++ b/mergetools/xxdiff @@ -0,0 +1,38 @@ +diff_cmd () { + "$merge_tool_path" \ + -R 'Accel.Search: "Ctrl+F"' \ + -R 'Accel.SearchForward: "Ctrl+G"' \ + "$LOCAL" "$REMOTE" + + # xxdiff can segfault on binary files which are often uninteresting. + # Do not allow segfaults to stop us from continuing on to the next file. + if test $? = 128 + then + return 1 + fi +} + +diff_cmd_help () { + echo "Use xxdiff (requires a graphical session)" +} + +merge_cmd () { + if $base_present + then + "$merge_tool_path" -X --show-merged-pane \ + -R 'Accel.SaveAsMerged: "Ctrl+S"' \ + -R 'Accel.Search: "Ctrl+F"' \ + -R 'Accel.SearchForward: "Ctrl+G"' \ + --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE" + else + "$merge_tool_path" -X $extra \ + -R 'Accel.SaveAsMerged: "Ctrl+S"' \ + -R 'Accel.Search: "Ctrl+F"' \ + -R 'Accel.SearchForward: "Ctrl+G"' \ + --merged-file "$MERGED" "$LOCAL" "$REMOTE" + fi +} + +merge_cmd_help () { + echo "Use xxdiff (requires a graphical session)" +} -- cgit v1.2.3