mirror of
https://github.com/git/git.git
synced 2024-11-07 09:43:00 +01:00
5c38ea31f3
'git difftool' is a git command that allows you to compare and edit files between revisions using common merge tools. '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. It uses the same configuration variables as 'git mergetool' and provides the same command-line interface as 'git diff'. Signed-off-by: David Aguilar <davvid@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
240 lines
5.9 KiB
Bash
Executable file
240 lines
5.9 KiB
Bash
Executable file
#!/bin/sh
|
|
# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
|
|
# It supports kdiff3, 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 () {
|
|
echo
|
|
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 SIGINT
|
|
|
|
# $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
|
|
;;
|
|
|
|
tkdiff)
|
|
"$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
|
|
;;
|
|
|
|
meld|vimdiff)
|
|
"$merge_tool_path" "$LOCAL" "$REMOTE"
|
|
;;
|
|
|
|
gvimdiff)
|
|
"$merge_tool_path" -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 mergetool.<tool>.cmd exists
|
|
valid_custom_tool() {
|
|
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 | 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 mergetool.<tool>.path configuration.
|
|
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
|
|
}
|
|
|
|
# Allow the GIT_MERGE_TOOL variable to provide a default value
|
|
test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
|
|
|
|
# If not merge tool was specified then use the merge.tool
|
|
# configuration variable. If that's invalid then reset merge_tool.
|
|
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
|
|
|
|
# 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
|
|
merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
|
|
# If gnome then prefer meld
|
|
if test -n "$GNOME_DESKTOP_SESSION_ID"; then
|
|
merge_tool_candidates="meld $merge_tool_candidates"
|
|
fi
|
|
# If KDE then prefer kdiff3
|
|
if test "$KDE_FULL_SESSION" = "true"; then
|
|
merge_tool_candidates="kdiff3 $merge_tool_candidates"
|
|
fi
|
|
fi
|
|
|
|
# $EDITOR is emacs so add emerge as a candidate
|
|
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
|
|
merge_tool_candidates="$merge_tool_candidates emerge"
|
|
fi
|
|
|
|
# $EDITOR is vim so add vimdiff as a candidate
|
|
if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
|
|
merge_tool_candidates="$merge_tool_candidates vimdiff"
|
|
fi
|
|
|
|
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
|
|
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
|