summaryrefslogtreecommitdiffstats
path: root/mergetools
diff options
context:
space:
mode:
Diffstat (limited to 'mergetools')
-rw-r--r--mergetools/araxis26
-rw-r--r--mergetools/bc37
-rw-r--r--mergetools/codecompare31
-rw-r--r--mergetools/deltawalker33
-rw-r--r--mergetools/diffmerge26
-rw-r--r--mergetools/diffuse23
-rw-r--r--mergetools/ecmerge22
-rw-r--r--mergetools/emerge34
-rw-r--r--mergetools/examdiff24
-rw-r--r--mergetools/guiffy26
-rw-r--r--mergetools/gvimdiff1
-rw-r--r--mergetools/kdiff344
-rw-r--r--mergetools/kompare19
-rw-r--r--mergetools/meld97
-rw-r--r--mergetools/nvimdiff1
-rw-r--r--mergetools/opendiff22
-rw-r--r--mergetools/p4merge44
-rw-r--r--mergetools/smerge20
-rw-r--r--mergetools/tkdiff24
-rw-r--r--mergetools/tortoisemerge40
-rw-r--r--mergetools/vimdiff638
-rw-r--r--mergetools/winmerge23
-rw-r--r--mergetools/xxdiff38
23 files changed, 1293 insertions, 0 deletions
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 <file>'
+ 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)"
+}