提交 bd15ef07 编写于 作者: J Junio C Hamano

Merge branch 'da/difftool'

* da/difftool:
  mergetool--lib: simplify API usage by removing more global variables
  Fix misspelled mergetool.keepBackup
  difftool/mergetool: refactor commands to use git-mergetool--lib
  mergetool: use $( ... ) instead of `backticks`
  bash completion: add git-difftool
  difftool: add support for a difftool.prompt config variable
  difftool: add various git-difftool tests
  difftool: move 'git-difftool' out of contrib
  difftool/mergetool: add diffuse as merge and diff tool
  difftool: add a -y shortcut for --no-prompt
  difftool: use perl built-ins when testing for msys
  difftool: remove the backup file feature
  difftool: remove merge options for opendiff, tkdiff, kdiff3 and xxdiff
  git-mergetool: add new merge tool TortoiseMerge
  git-mergetool/difftool: make (g)vimdiff workable under Windows
  doc/merge-config: list ecmerge as a built-in merge tool
......@@ -36,6 +36,8 @@ git-diff
git-diff-files
git-diff-index
git-diff-tree
git-difftool
git-difftool--helper
git-describe
git-fast-export
git-fast-import
......@@ -79,6 +81,7 @@ git-merge-recursive
git-merge-resolve
git-merge-subtree
git-mergetool
git-mergetool--lib
git-mktag
git-mktree
git-name-rev
......
......@@ -667,6 +667,27 @@ diff.suppressBlankEmpty::
A boolean to inhibit the standard behavior of printing a space
before each empty output line. Defaults to false.
diff.tool::
Controls which diff tool is used. `diff.tool` overrides
`merge.tool` when used by linkgit:git-difftool[1] and has
the same valid values as `merge.tool` minus "tortoisemerge"
and plus "kompare".
difftool.<tool>.path::
Override the path for the given tool. This is useful in case
your tool is not in the PATH.
difftool.<tool>.cmd::
Specify the command to invoke the specified diff tool.
The specified command is evaluated in shell with the following
variables available: 'LOCAL' is set to the name of the temporary
file containing the contents of the diff pre-image and 'REMOTE'
is set to the name of the temporary file containing the contents
of the diff post-image.
difftool.prompt::
Prompt before each invocation of the diff tool.
diff.wordRegex::
A POSIX Extended Regular Expression used to determine what is a "word"
when performing word-by-word difference calculations. Character
......
......@@ -3,35 +3,37 @@ git-difftool(1)
NAME
----
git-difftool - compare changes using common merge tools
git-difftool - Show changes using common diff tools
SYNOPSIS
--------
'git difftool' [--tool=<tool>] [--no-prompt] ['git diff' options]
'git difftool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<'git diff' options>]
DESCRIPTION
-----------
'git-difftool' is a git command that allows you to compare and edit files
between revisions using common merge tools. At its most basic level,
'git-difftool' does what 'git-mergetool' does but its use is for non-merge
situations such as when preparing commits or comparing changes against
the index.
'git difftool' is a frontend to 'git diff' and accepts the same
arguments and options.
See linkgit:git-diff[1] for the full list of supported options.
between revisions using common diff tools. 'git difftool' is a frontend
to 'git-diff' and accepts the same options and arguments.
OPTIONS
-------
-y::
--no-prompt::
Do not prompt before launching a diff tool.
--prompt::
Prompt before each invocation of the diff tool.
This is the default behaviour; the option is provided to
override any configuration settings.
-t <tool>::
--tool=<tool>::
Use the merge resolution program specified by <tool>.
Use the diff tool specified by <tool>.
Valid merge tools are:
kdiff3, kompare, tkdiff, meld, xxdiff, emerge,
vimdiff, gvimdiff, ecmerge, and opendiff
kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff,
ecmerge, diffuse and opendiff
+
If a merge resolution program is not specified, 'git-difftool'
If a diff tool is not specified, 'git-difftool'
will use the configuration variable `diff.tool`. If the
configuration variable `diff.tool` is not set, 'git-difftool'
will pick a suitable default.
......@@ -42,7 +44,7 @@ can configure the absolute path to kdiff3 by setting
`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
tool is available in PATH.
+
Instead of running one of the known merge tool programs,
Instead of running one of the known diff tools,
'git-difftool' can be customized to run an alternative program
by specifying the command line to invoke in a configuration
variable `difftool.<tool>.cmd`.
......@@ -56,8 +58,7 @@ is set to the name of the temporary file containing the contents
of the diff post-image. `$BASE` is provided for compatibility
with custom merge tool commands and has the same value as `$LOCAL`.
--no-prompt::
Do not prompt before launching a diff tool.
See linkgit:git-diff[1] for the full list of supported options.
CONFIG VARIABLES
----------------
......@@ -65,20 +66,19 @@ CONFIG VARIABLES
difftool equivalents have not been defined.
diff.tool::
The default merge tool to use.
The default diff tool to use.
difftool.<tool>.path::
Override the path for the given tool. This is useful in case
your tool is not in the PATH.
difftool.<tool>.cmd::
Specify the command to invoke the specified merge tool.
Specify the command to invoke the specified diff tool.
+
See the `--tool=<tool>` option above for more details.
merge.keepBackup::
The original, unedited file content can be saved to a file with
a `.orig` extension. Defaults to `true` (i.e. keep the backup files).
difftool.prompt::
Prompt before each invocation of the diff tool.
SEE ALSO
--------
......
git-mergetool--lib(1)
=====================
NAME
----
git-mergetool--lib - Common git merge tool shell scriptlets
SYNOPSIS
--------
'TOOL_MODE=(diff|merge) . "$(git --exec-path)/git-mergetool--lib"'
DESCRIPTION
-----------
This is not a command the end user would want to run. Ever.
This documentation is meant for people who are studying the
Porcelain-ish scripts and/or are writing new ones.
The 'git-mergetool--lib' scriptlet is designed to be sourced (using
`.`) by other shell scripts to set up functions for working
with git merge tools.
Before sourcing 'git-mergetool--lib', your script must set `TOOL_MODE`
to define the operation mode for the functions listed below.
'diff' and 'merge' are valid values.
FUNCTIONS
---------
get_merge_tool::
returns a merge tool.
get_merge_tool_cmd::
returns the custom command for a merge tool.
get_merge_tool_path::
returns the custom path for a merge tool.
run_merge_tool::
launches a merge tool given the tool name and a true/false
flag to indicate whether a merge base is present.
'$MERGED', '$LOCAL', '$REMOTE', and '$BASE' must be defined
for use by the merge tool.
Author
------
Written by David Aguilar <davvid@gmail.com>
Documentation
--------------
Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
GIT
---
Part of the linkgit:git[1] suite
......@@ -26,7 +26,8 @@ OPTIONS
--tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge,
diffuse, tortoisemerge and opendiff
+
If a merge resolution program is not specified, 'git-mergetool'
will use the configuration variable `merge.tool`. If the
......
......@@ -22,7 +22,8 @@ merge.stat::
merge.tool::
Controls which merge resolution program is used by
linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3",
"tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
"tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff",
"diffuse", "ecmerge", "tortoisemerge", and
"opendiff". Any other value is treated is custom merge tool
and there must be a corresponding mergetool.<tool>.cmd option.
......
......@@ -279,12 +279,14 @@ TEST_PROGRAMS =
SCRIPT_SH += git-am.sh
SCRIPT_SH += git-bisect.sh
SCRIPT_SH += git-difftool--helper.sh
SCRIPT_SH += git-filter-branch.sh
SCRIPT_SH += git-lost-found.sh
SCRIPT_SH += git-merge-octopus.sh
SCRIPT_SH += git-merge-one-file.sh
SCRIPT_SH += git-merge-resolve.sh
SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-mergetool--lib.sh
SCRIPT_SH += git-parse-remote.sh
SCRIPT_SH += git-pull.sh
SCRIPT_SH += git-quiltimport.sh
......@@ -298,6 +300,7 @@ SCRIPT_SH += git-submodule.sh
SCRIPT_SH += git-web--browse.sh
SCRIPT_PERL += git-add--interactive.perl
SCRIPT_PERL += git-difftool.perl
SCRIPT_PERL += git-archimport.perl
SCRIPT_PERL += git-cvsexportcommit.perl
SCRIPT_PERL += git-cvsimport.perl
......
......@@ -33,6 +33,7 @@ git-diff mainporcelain common
git-diff-files plumbinginterrogators
git-diff-index plumbinginterrogators
git-diff-tree plumbinginterrogators
git-difftool ancillaryinterrogators
git-fast-export ancillarymanipulators
git-fast-import ancillarymanipulators
git-fetch mainporcelain common
......
......@@ -910,6 +910,26 @@ _git_diff ()
__git_complete_file
}
__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
tkdiff vimdiff gvimdiff xxdiff
"
_git_difftool ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--tool=*)
__gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
return
;;
--*)
__gitcomp "--tool="
return
;;
esac
COMPREPLY=()
}
__git_fetch_options="
--quiet --verbose --append --upload-pack --force --keep --depth=
--tags --no-tags
......@@ -1172,10 +1192,7 @@ _git_mergetool ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--tool=*)
__gitcomp "
kdiff3 tkdiff meld xxdiff emerge
vimdiff gvimdiff ecmerge opendiff
" "" "${cur##--tool=}"
__gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
return
;;
--*)
......@@ -1901,6 +1918,7 @@ _git ()
config) _git_config ;;
describe) _git_describe ;;
diff) _git_diff ;;
difftool) _git_difftool ;;
fetch) _git_fetch ;;
format-patch) _git_format_patch ;;
fsck) _git_fsck ;;
......
#!/bin/sh
# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff,
# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools.
# This script is typically launched by using the 'git difftool'
# convenience command.
#
# Copyright (c) 2009 David Aguilar
# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
should_prompt () {
! test -n "$GIT_DIFFTOOL_NO_PROMPT"
}
# Should we keep the backup .orig file?
keep_backup_mode="$(git config --bool merge.keepBackup || echo true)"
keep_backup () {
test "$keep_backup_mode" = "true"
}
# This function manages the backup .orig file.
# A backup $MERGED.orig file is created if changes are detected.
cleanup_temp_files () {
if test -n "$MERGED"; then
if keep_backup && test "$MERGED" -nt "$BACKUP"; then
test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
else
rm -f -- "$BACKUP"
fi
fi
}
# This is called when users Ctrl-C out of git-difftool-helper
sigint_handler () {
cleanup_temp_files
exit 1
}
# This function prepares temporary files and launches the appropriate
# merge tool.
launch_merge_tool () {
# Merged is the filename as it appears in the work tree
# Local is the contents of a/filename
# Remote is the contents of b/filename
# Custom merge tool commands might use $BASE so we provide it
MERGED="$1"
LOCAL="$2"
REMOTE="$3"
BASE="$1"
ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
BACKUP="$MERGED.BACKUP.$ext"
# Create and ensure that we clean up $BACKUP
test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
trap sigint_handler INT
# $LOCAL and $REMOTE are temporary files so prompt
# the user with the real $MERGED name before launching $merge_tool.
if should_prompt; then
printf "\nViewing: '$MERGED'\n"
printf "Hit return to launch '%s': " "$merge_tool"
read ans
fi
# Run the appropriate merge tool command
case "$merge_tool" in
kdiff3)
basename=$(basename "$MERGED")
"$merge_tool_path" --auto \
--L1 "$basename (A)" \
--L2 "$basename (B)" \
-o "$MERGED" "$LOCAL" "$REMOTE" \
> /dev/null 2>&1
;;
kompare)
"$merge_tool_path" "$LOCAL" "$REMOTE"
;;
tkdiff)
"$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
;;
meld)
"$merge_tool_path" "$LOCAL" "$REMOTE"
;;
vimdiff)
"$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE"
;;
gvimdiff)
"$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE"
;;
xxdiff)
"$merge_tool_path" \
-X \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$MERGED" \
"$LOCAL" "$REMOTE"
;;
opendiff)
"$merge_tool_path" "$LOCAL" "$REMOTE" \
-merge "$MERGED" | cat
;;
ecmerge)
"$merge_tool_path" "$LOCAL" "$REMOTE" \
--default --mode=merge2 --to="$MERGED"
;;
emerge)
"$merge_tool_path" -f emerge-files-command \
"$LOCAL" "$REMOTE" "$(basename "$MERGED")"
;;
*)
if test -n "$merge_tool_cmd"; then
( eval $merge_tool_cmd )
fi
;;
esac
cleanup_temp_files
}
# Verifies that (difftool|mergetool).<tool>.cmd exists
valid_custom_tool() {
merge_tool_cmd="$(git config difftool.$1.cmd)"
test -z "$merge_tool_cmd" &&
merge_tool_cmd="$(git config mergetool.$1.cmd)"
test -n "$merge_tool_cmd"
}
# Verifies that the chosen merge tool is properly setup.
# Built-in merge tools are always valid.
valid_tool() {
case "$1" in
kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
;; # happy
*)
if ! valid_custom_tool "$1"
then
return 1
fi
;;
esac
}
# Sets up the merge_tool_path variable.
# This handles the difftool.<tool>.path configuration.
# This also falls back to mergetool defaults.
init_merge_tool_path() {
merge_tool_path=$(git config difftool."$1".path)
test -z "$merge_tool_path" &&
merge_tool_path=$(git config mergetool."$1".path)
if test -z "$merge_tool_path"; then
case "$1" in
emerge)
merge_tool_path=emacs
;;
*)
merge_tool_path="$1"
;;
esac
fi
}
# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values
test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL"
# If merge tool was not specified then use the diff.tool
# configuration variable. If that's invalid then reset merge_tool.
# Fallback to merge.tool.
if test -z "$merge_tool"; then
merge_tool=$(git config diff.tool)
test -z "$merge_tool" &&
merge_tool=$(git config merge.tool)
if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
echo >&2 "git config option diff.tool set to unknown tool: $merge_tool"
echo >&2 "Resetting to default..."
unset merge_tool
fi
fi
# Try to guess an appropriate merge tool if no tool has been set.
if test -z "$merge_tool"; then
# We have a $DISPLAY so try some common UNIX merge tools
if test -n "$DISPLAY"; then
# If gnome then prefer meld, otherwise, prefer kdiff3 or kompare
if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff"
else
merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff"
fi
fi
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
# $EDITOR is emacs so add emerge as a candidate
merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
# $EDITOR is vim so add vimdiff as a candidate
merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
else
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
fi
echo "merge tool candidates: $merge_tool_candidates"
# Loop over each candidate and stop when a valid merge tool is found.
for i in $merge_tool_candidates
do
init_merge_tool_path $i
if type "$merge_tool_path" > /dev/null 2>&1; then
merge_tool=$i
break
fi
done
if test -z "$merge_tool" ; then
echo "No known merge resolution program available."
exit 1
fi
else
# A merge tool has been set, so verify that it's valid.
if ! valid_tool "$merge_tool"; then
echo >&2 "Unknown merge tool $merge_tool"
exit 1
fi
init_merge_tool_path "$merge_tool"
if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
exit 1
fi
fi
# Launch the merge tool on each path provided by 'git diff'
while test $# -gt 6
do
launch_merge_tool "$1" "$2" "$5"
shift 7
done
#!/bin/sh
# git-difftool--helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
# This script is typically launched by using the 'git difftool'
# convenience command.
#
# Copyright (c) 2009 David Aguilar
# Load common functions from git-mergetool--lib
TOOL_MODE=diff
. git-mergetool--lib
# difftool.prompt controls the default prompt/no-prompt behavior
# and is overridden with $GIT_DIFFTOOL*_PROMPT.
should_prompt () {
prompt=$(git config --bool difftool.prompt || echo true)
if test "$prompt" = true; then
test -z "$GIT_DIFFTOOL_NO_PROMPT"
else
test -n "$GIT_DIFFTOOL_PROMPT"
fi
}
# Sets up shell variables and runs a merge tool
launch_merge_tool () {
# Merged is the filename as it appears in the work tree
# Local is the contents of a/filename
# Remote is the contents of b/filename
# Custom merge tool commands might use $BASE so we provide it
MERGED="$1"
LOCAL="$2"
REMOTE="$3"
BASE="$1"
# $LOCAL and $REMOTE are temporary files so prompt
# the user with the real $MERGED name before launching $merge_tool.
if should_prompt; then
printf "\nViewing: '$MERGED'\n"
printf "Hit return to launch '%s': " "$merge_tool"
read ans
fi
# Run the appropriate merge tool command
run_merge_tool "$merge_tool"
}
# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values
test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL"
if test -z "$merge_tool"; then
merge_tool="$(get_merge_tool)" || exit
fi
# Launch the merge tool on each path provided by 'git diff'
while test $# -gt 6
do
launch_merge_tool "$1" "$2" "$5"
shift 7
done
......@@ -2,9 +2,12 @@
# Copyright (c) 2009 David Aguilar
#
# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
# git-difftool-helper script. This script exports
# GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and
# GIT_DIFFTOOL_NO_PROMPT and GIT_DIFF_TOOL for use by git-difftool-helper.
# git-difftool--helper script.
#
# This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, and GIT_DIFF_TOOL
# are exported for use by git-difftool--helper.
#
# Any arguments that are unknown to this script are forwarded to 'git diff'.
use strict;
......@@ -18,7 +21,7 @@
sub usage
{
print << 'USAGE';
usage: git difftool [--tool=<tool>] [--no-prompt] ["git diff" options]
usage: git difftool [--tool=<tool>] [-y|--no-prompt] ["git diff" options]
USAGE
exit 1;
}
......@@ -27,13 +30,16 @@ sub setup_environment
{
$ENV{PATH} = "$DIR:$ENV{PATH}";
$ENV{GIT_PAGER} = '';
$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper';
$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
}
sub exe
{
my $exe = shift;
return defined $ENV{COMSPEC} ? "$exe.exe" : $exe;
if ($^O eq 'MSWin32' || $^O eq 'msys') {
return "$exe.exe";
}
return $exe;
}
sub generate_command
......@@ -47,7 +53,7 @@ sub generate_command
$skip_next = 0;
next;
}
if ($arg eq '-t' or $arg eq '--tool') {
if ($arg eq '-t' || $arg eq '--tool') {
usage() if $#ARGV <= $idx;
$ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];
$skip_next = 1;
......@@ -57,11 +63,17 @@ sub generate_command
$ENV{GIT_DIFF_TOOL} = substr($arg, 7);
next;
}
if ($arg eq '--no-prompt') {
if ($arg eq '-y' || $arg eq '--no-prompt') {
$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
delete $ENV{GIT_DIFFTOOL_PROMPT};
next;
}
if ($arg eq '--prompt') {
$ENV{GIT_DIFFTOOL_PROMPT} = 'true';
delete $ENV{GIT_DIFFTOOL_NO_PROMPT};
next;
}
if ($arg eq '-h' or $arg eq '--help') {
if ($arg eq '-h' || $arg eq '--help') {
usage();
}
push @command, $arg;
......
# git-mergetool--lib is a library for common merge tool functions
diff_mode() {
test "$TOOL_MODE" = diff
}
merge_mode() {
test "$TOOL_MODE" = merge
}
translate_merge_tool_path () {
case "$1" in
vimdiff)
echo vim
;;
gvimdiff)
echo gvim
;;
emerge)
echo emacs
;;
*)
echo "$1"
;;
esac
}
check_unchanged () {
if test "$MERGED" -nt "$BACKUP"; then
status=0
else
while true; do
echo "$MERGED seems unchanged."
printf "Was the merge successful? [y/n] "
read answer < /dev/tty
case "$answer" in
y*|Y*) status=0; break ;;
n*|N*) status=1; break ;;
esac
done
fi
}
valid_tool () {
case "$1" in
kdiff3 | tkdiff | xxdiff | meld | opendiff | \
emerge | vimdiff | gvimdiff | ecmerge | diffuse)
;; # happy
tortoisemerge)
if ! merge_mode; then
return 1
fi
;;
kompare)
if ! diff_mode; then
return 1
fi
;;
*)
if test -z "$(get_merge_tool_cmd "$1")"; then
return 1
fi
;;
esac
}
get_merge_tool_cmd () {
# Prints the custom command for a merge tool
if test -n "$1"; then
merge_tool="$1"
else
merge_tool="$(get_merge_tool)"
fi
if diff_mode; then
echo "$(git config difftool.$merge_tool.cmd ||
git config mergetool.$merge_tool.cmd)"
else
echo "$(git config mergetool.$merge_tool.cmd)"
fi
}
run_merge_tool () {
merge_tool_path="$(get_merge_tool_path "$1")" || exit
base_present="$2"
status=0
case "$1" in
kdiff3)
if merge_mode; then
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
status=$?
else
("$merge_tool_path" --auto \
--L1 "$MERGED (A)" \
--L2 "$MERGED (B)" "$LOCAL" "$REMOTE" \
> /dev/null 2>&1)
fi
;;
kompare)
"$merge_tool_path" "$LOCAL" "$REMOTE"
;;
tkdiff)
if merge_mode; then
if $base_present; then
"$merge_tool_path" -a "$BASE" \
-o "$MERGED" "$LOCAL" "$REMOTE"
else
"$merge_tool_path" \
-o "$MERGED" "$LOCAL" "$REMOTE"
fi
status=$?
else
"$merge_tool_path" "$LOCAL" "$REMOTE"
fi
;;
meld)
if merge_mode; then
touch "$BACKUP"
"$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
check_unchanged
else
"$merge_tool_path" "$LOCAL" "$REMOTE"
fi
;;
diffuse)
if merge_mode; then
touch "$BACKUP"
if $base_present; then
"$merge_tool_path" \
"$LOCAL" "$MERGED" "$REMOTE" \
"$BASE" | cat
else
"$merge_tool_path" \
"$LOCAL" "$MERGED" "$REMOTE" | cat
fi
check_unchanged
else
"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
fi
;;
vimdiff)
if merge_mode; then
touch "$BACKUP"
"$merge_tool_path" -d -c "wincmd l" \
"$LOCAL" "$MERGED" "$REMOTE"
check_unchanged
else
"$merge_tool_path" -d -c "wincmd l" \
"$LOCAL" "$REMOTE"
fi
;;
gvimdiff)
if merge_mode; then
touch "$BACKUP"
"$merge_tool_path" -d -c "wincmd l" -f \
"$LOCAL" "$MERGED" "$REMOTE"
check_unchanged
else
"$merge_tool_path" -d -c "wincmd l" -f \
"$LOCAL" "$REMOTE"
fi
;;
xxdiff)
if merge_mode; then
touch "$BACKUP"
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
check_unchanged
else
"$merge_tool_path" \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
"$LOCAL" "$REMOTE"
fi
;;
opendiff)
if merge_mode; then
touch "$BACKUP"
if $base_present; then
"$merge_tool_path" "$LOCAL" "$REMOTE" \
-ancestor "$BASE" \
-merge "$MERGED" | cat
else
"$merge_tool_path" "$LOCAL" "$REMOTE" \
-merge "$MERGED" | cat
fi
check_unchanged
else
"$merge_tool_path" "$LOCAL" "$REMOTE" | cat
fi
;;
ecmerge)
if merge_mode; then
touch "$BACKUP"
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
check_unchanged
else
"$merge_tool_path" "$LOCAL" "$REMOTE" \
--default --mode=merge2 --to="$MERGED"
fi
;;
emerge)
if merge_mode; then
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
status=$?
else
"$merge_tool_path" -f emerge-files-command \
"$LOCAL" "$REMOTE" "$(basename "$MERGED")"
fi
;;
tortoisemerge)
if $base_present; then
touch "$BACKUP"
"$merge_tool_path" \
-base:"$BASE" -mine:"$LOCAL" \
-theirs:"$REMOTE" -merged:"$MERGED"
check_unchanged
else
echo "TortoiseMerge cannot be used without a base" 1>&2
status=1
fi
;;
*)
merge_tool_cmd="$(get_merge_tool_cmd "$1")"
if test -z "$merge_tool_cmd"; then
if merge_mode; then
status=1
fi
break
fi
if merge_mode; then
trust_exit_code="$(git config --bool \
mergetool."$1".trustExitCode || echo false)"
if test "$trust_exit_code" = "false"; then
touch "$BACKUP"
( eval $merge_tool_cmd )
check_unchanged
else
( eval $merge_tool_cmd )
status=$?
fi
else
( eval $merge_tool_cmd )
fi
;;
esac
return $status
}
guess_merge_tool () {
if merge_mode; then
tools="tortoisemerge"
else
tools="kompare"
fi
if test -n "$DISPLAY"; then
if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
tools="meld opendiff kdiff3 tkdiff xxdiff $tools"
else
tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
fi
tools="$tools gvimdiff diffuse ecmerge"
fi
if echo "${VISUAL:-$EDITOR}" | grep emacs > /dev/null 2>&1; then
# $EDITOR is emacs so add emerge as a candidate
tools="$tools emerge vimdiff"
elif echo "${VISUAL:-$EDITOR}" | grep vim > /dev/null 2>&1; then
# $EDITOR is vim so add vimdiff as a candidate
tools="$tools vimdiff emerge"
else
tools="$tools emerge vimdiff"
fi
echo >&2 "merge tool candidates: $tools"
# Loop over each candidate and stop when a valid merge tool is found.
for i in $tools
do
merge_tool_path="$(translate_merge_tool_path "$i")"
if type "$merge_tool_path" > /dev/null 2>&1; then
echo "$i"
return 0
fi
done
echo >&2 "No known merge resolution program available."
return 1
}
get_configured_merge_tool () {
# Diff mode first tries diff.tool and falls back to merge.tool.
# Merge mode only checks merge.tool
if diff_mode; then
merge_tool=$(git config diff.tool || git config merge.tool)
else
merge_tool=$(git config merge.tool)
fi
if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
echo >&2 "git config option $TOOL_MODE.tool set to unknown tool: $merge_tool"
echo >&2 "Resetting to default..."
return 1
fi
echo "$merge_tool"
}
get_merge_tool_path () {
# A merge tool has been set, so verify that it's valid.
if test -n "$1"; then
merge_tool="$1"
else
merge_tool="$(get_merge_tool)"
fi
if ! valid_tool "$merge_tool"; then
echo >&2 "Unknown merge tool $merge_tool"
exit 1
fi
if diff_mode; then
merge_tool_path=$(git config difftool."$merge_tool".path ||
git config mergetool."$merge_tool".path)
else
merge_tool_path=$(git config mergetool."$merge_tool".path)
fi
if test -z "$merge_tool_path"; then
merge_tool_path="$(translate_merge_tool_path "$merge_tool")"
fi
if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
! type "$merge_tool_path" > /dev/null 2>&1; then
echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
"'$merge_tool_path'"
exit 1
fi
echo "$merge_tool_path"
}
get_merge_tool () {
# Check if a merge tool has been configured
merge_tool=$(get_configured_merge_tool)
# Try to guess an appropriate merge tool if no tool has been set.
if test -z "$merge_tool"; then
merge_tool="$(guess_merge_tool)" || exit
fi
echo "$merge_tool"
}
......@@ -11,7 +11,9 @@
USAGE='[--tool=tool] [-y|--no-prompt|--prompt] [file to merge] ...'
SUBDIRECTORY_OK=Yes
OPTIONS_SPEC=
TOOL_MODE=merge
. git-sh-setup
. git-mergetool--lib
require_work_tree
# Returns true if the mode reflects a symlink
......@@ -110,22 +112,6 @@ resolve_deleted_merge () {
done
}
check_unchanged () {
if test "$MERGED" -nt "$BACKUP" ; then
status=0;
else
while true; do
echo "$MERGED seems unchanged."
printf "Was the merge successful? [y/n] "
read answer < /dev/tty
case "$answer" in
y*|Y*) status=0; break ;;
n*|N*) status=1; break ;;
esac
done
fi
}
checkout_staged_file () {
tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^ ]*\) ')
......@@ -137,7 +123,7 @@ checkout_staged_file () {
merge_file () {
MERGED="$1"
f=`git ls-files -u -- "$MERGED"`
f=$(git ls-files -u -- "$MERGED")
if test -z "$f" ; then
if test ! -f "$MERGED" ; then
echo "$MERGED: file not found"
......@@ -156,9 +142,9 @@ merge_file () {
mv -- "$MERGED" "$BACKUP"
cp -- "$BACKUP" "$MERGED"
base_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}'`
local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
base_present && checkout_staged_file 1 "$MERGED" "$BASE"
local_present && checkout_staged_file 2 "$MERGED" "$LOCAL"
......@@ -188,97 +174,13 @@ merge_file () {
read ans
fi
case "$merge_tool" in
kdiff3)
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
status=$?
;;
tkdiff)
if base_present ; then
"$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"
else
"$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
fi
status=$?
;;
meld)
touch "$BACKUP"
"$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
check_unchanged
;;
vimdiff)
touch "$BACKUP"
"$merge_tool_path" -c "wincmd l" "$LOCAL" "$MERGED" "$REMOTE"
check_unchanged
;;
gvimdiff)
touch "$BACKUP"
"$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$MERGED" "$REMOTE"
check_unchanged
;;
xxdiff)
touch "$BACKUP"
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 --show-merged-pane \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$MERGED" "$LOCAL" "$REMOTE"
fi
check_unchanged
;;
opendiff)
touch "$BACKUP"
if base_present; then
"$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED" | cat
else
"$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED" | cat
fi
check_unchanged
;;
ecmerge)
touch "$BACKUP"
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
check_unchanged
;;
emerge)
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
status=$?
;;
*)
if test -n "$merge_tool_cmd"; then
if test "$merge_tool_trust_exit_code" = "false"; then
touch "$BACKUP"
( eval $merge_tool_cmd )
check_unchanged
else
( eval $merge_tool_cmd )
status=$?
fi
fi
;;
esac
if test "$status" -ne 0; then
if base_present; then
present=true
else
present=false
fi
if ! run_merge_tool "$merge_tool" "$present"; then
echo "merge of $MERGED failed" 1>&2
mv -- "$BACKUP" "$MERGED"
......@@ -308,7 +210,7 @@ do
-t|--tool*)
case "$#,$1" in
*,*=*)
merge_tool=`expr "z$1" : 'z-[^=]*=\(.*\)'`
merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
;;
1,*)
usage ;;
......@@ -337,38 +239,6 @@ do
shift
done
valid_custom_tool()
{
merge_tool_cmd="$(git config mergetool.$1.cmd)"
test -n "$merge_tool_cmd"
}
valid_tool() {
case "$1" in
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
;; # happy
*)
if ! valid_custom_tool "$1"; then
return 1
fi
;;
esac
}
init_merge_tool_path() {
merge_tool_path=`git config mergetool.$1.path`
if test -z "$merge_tool_path" ; then
case "$1" in
emerge)
merge_tool_path=emacs
;;
*)
merge_tool_path=$1
;;
esac
fi
}
prompt_after_failed_merge() {
while true; do
printf "Continue merging other unresolved paths (y/n) ? "
......@@ -387,67 +257,16 @@ prompt_after_failed_merge() {
}
if test -z "$merge_tool"; then
merge_tool=`git config merge.tool`
if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
echo >&2 "Resetting to default..."
unset merge_tool
fi
fi
if test -z "$merge_tool" ; then
if test -n "$DISPLAY"; then
if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
merge_tool_candidates="meld kdiff3 tkdiff xxdiff gvimdiff"
else
merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
fi
fi
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
else
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
fi
echo "merge tool candidates: $merge_tool_candidates"
for i in $merge_tool_candidates; do
init_merge_tool_path $i
if type "$merge_tool_path" > /dev/null 2>&1; then
merge_tool=$i
break
fi
done
if test -z "$merge_tool" ; then
echo "No known merge resolution program available."
exit 1
fi
else
if ! valid_tool "$merge_tool"; then
echo >&2 "Unknown merge_tool $merge_tool"
exit 1
fi
init_merge_tool_path "$merge_tool"
merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
exit 1
fi
if ! test -z "$merge_tool_cmd"; then
merge_tool_trust_exit_code="$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)"
fi
merge_tool=$(get_merge_tool "$merge_tool") || exit
fi
merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
last_status=0
rollup_status=0
if test $# -eq 0 ; then
files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u`
files=$(git ls-files -u | sed -e 's/^[^ ]* //' | sort -u)
if test -z "$files" ; then
echo "No files need merging"
exit 0
......
#!/bin/sh
#
# Copyright (c) 2009 David Aguilar
#
test_description='git-difftool
Testing basic diff tool invocation
'
. ./test-lib.sh
remove_config_vars()
{
# Unset all config variables used by git-difftool
git config --unset diff.tool
git config --unset difftool.test-tool.cmd
git config --unset difftool.prompt
git config --unset merge.tool
git config --unset mergetool.test-tool.cmd
return 0
}
restore_test_defaults()
{
# Restores the test defaults used by several tests
remove_config_vars
unset GIT_DIFF_TOOL
unset GIT_MERGE_TOOL
unset GIT_DIFFTOOL_PROMPT
unset GIT_DIFFTOOL_NO_PROMPT
git config diff.tool test-tool &&
git config difftool.test-tool.cmd 'cat $LOCAL'
}
prompt_given()
{
prompt="$1"
test "$prompt" = "Hit return to launch 'test-tool': branch"
}
# Create a file on master and change it on branch
test_expect_success 'setup' '
echo master >file &&
git add file &&
git commit -m "added file" &&
git checkout -b branch master &&
echo branch >file &&
git commit -a -m "branch changed file" &&
git checkout master
'
# Configure a custom difftool.<tool>.cmd and use it
test_expect_success 'custom commands' '
restore_test_defaults &&
git config difftool.test-tool.cmd "cat \$REMOTE" &&
diff=$(git difftool --no-prompt branch) &&
test "$diff" = "master" &&
restore_test_defaults &&
diff=$(git difftool --no-prompt branch) &&
test "$diff" = "branch"
'
# Ensures that git-difftool ignores bogus --tool values
test_expect_success 'difftool ignores bad --tool values' '
diff=$(git difftool --no-prompt --tool=bogus-tool branch)
test "$?" = 1 &&
test "$diff" = ""
'
# Specify the diff tool using $GIT_DIFF_TOOL
test_expect_success 'GIT_DIFF_TOOL variable' '
git config --unset diff.tool
GIT_DIFF_TOOL=test-tool &&
export GIT_DIFF_TOOL &&
diff=$(git difftool --no-prompt branch) &&
test "$diff" = "branch" &&
restore_test_defaults
'
# Test the $GIT_*_TOOL variables and ensure
# that $GIT_DIFF_TOOL always wins unless --tool is specified
test_expect_success 'GIT_DIFF_TOOL overrides' '
git config diff.tool bogus-tool &&
git config merge.tool bogus-tool &&
GIT_MERGE_TOOL=test-tool &&
export GIT_MERGE_TOOL &&
diff=$(git difftool --no-prompt branch) &&
test "$diff" = "branch" &&
unset GIT_MERGE_TOOL &&
GIT_MERGE_TOOL=bogus-tool &&
GIT_DIFF_TOOL=test-tool &&
export GIT_MERGE_TOOL &&
export GIT_DIFF_TOOL &&
diff=$(git difftool --no-prompt branch) &&
test "$diff" = "branch" &&
GIT_DIFF_TOOL=bogus-tool &&
export GIT_DIFF_TOOL &&
diff=$(git difftool --no-prompt --tool=test-tool branch) &&
test "$diff" = "branch" &&
restore_test_defaults
'
# Test that we don't have to pass --no-prompt to difftool
# when $GIT_DIFFTOOL_NO_PROMPT is true
test_expect_success 'GIT_DIFFTOOL_NO_PROMPT variable' '
GIT_DIFFTOOL_NO_PROMPT=true &&
export GIT_DIFFTOOL_NO_PROMPT &&
diff=$(git difftool branch) &&
test "$diff" = "branch" &&
restore_test_defaults
'
# git-difftool supports the difftool.prompt variable.
# Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
git config difftool.prompt false &&
GIT_DIFFTOOL_PROMPT=true &&
export GIT_DIFFTOOL_PROMPT &&
prompt=$(echo | git difftool --prompt branch | tail -1) &&
prompt_given "$prompt" &&
restore_test_defaults
'
# Test that we don't have to pass --no-prompt when difftool.prompt is false
test_expect_success 'difftool.prompt config variable is false' '
git config difftool.prompt false &&
diff=$(git difftool branch) &&
test "$diff" = "branch" &&
restore_test_defaults
'
# Test that the -y flag can override difftool.prompt = true
test_expect_success 'difftool.prompt can overridden with -y' '
git config difftool.prompt true &&
diff=$(git difftool -y branch) &&
test "$diff" = "branch" &&
restore_test_defaults
'
# Test that the --prompt flag can override difftool.prompt = false
test_expect_success 'difftool.prompt can overridden with --prompt' '
git config difftool.prompt false &&
prompt=$(echo | git difftool --prompt branch | tail -1) &&
prompt_given "$prompt" &&
restore_test_defaults
'
# Test that the last flag passed on the command-line wins
test_expect_success 'difftool last flag wins' '
diff=$(git difftool --prompt --no-prompt branch) &&
test "$diff" = "branch" &&
restore_test_defaults &&
prompt=$(echo | git difftool --no-prompt --prompt branch | tail -1) &&
prompt_given "$prompt" &&
restore_test_defaults
'
# git-difftool falls back to git-mergetool config variables
# so test that behavior here
test_expect_success 'difftool + mergetool config variables' '
remove_config_vars
git config merge.tool test-tool &&
git config mergetool.test-tool.cmd "cat \$LOCAL" &&
diff=$(git difftool --no-prompt branch) &&
test "$diff" = "branch" &&
# set merge.tool to something bogus, diff.tool to test-tool
git config merge.tool bogus-tool &&
git config diff.tool test-tool &&
diff=$(git difftool --no-prompt branch) &&
test "$diff" = "branch" &&
restore_test_defaults
'
test_expect_success 'difftool.<tool>.path' '
git config difftool.tkdiff.path echo &&
diff=$(git difftool --tool=tkdiff --no-prompt branch) &&
git config --unset difftool.tkdiff.path &&
lines=$(echo "$diff" | grep file | wc -l) &&
test "$lines" -eq 1
'
test_done
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册