-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
contrib: add 'git difftool' for launching common merge tools
'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>
- Loading branch information
David Aguilar
authored and
Junio C Hamano
committed
Jan 18, 2009
1 parent
3d27986
commit 5c38ea3
Showing
3 changed files
with
418 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#!/usr/bin/env perl | ||
# 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_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. | ||
# Any arguments that are unknown to this script are forwarded to 'git diff'. | ||
|
||
use strict; | ||
use warnings; | ||
use Cwd qw(abs_path); | ||
use File::Basename qw(dirname); | ||
|
||
my $DIR = abs_path(dirname($0)); | ||
|
||
|
||
sub usage | ||
{ | ||
print << 'USAGE'; | ||
usage: git difftool [--no-prompt] [--tool=tool] ["git diff" options] | ||
USAGE | ||
exit 1; | ||
} | ||
|
||
sub setup_environment | ||
{ | ||
$ENV{PATH} = "$DIR:$ENV{PATH}"; | ||
$ENV{GIT_PAGER} = ''; | ||
$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper'; | ||
} | ||
|
||
sub exe | ||
{ | ||
my $exe = shift; | ||
return defined $ENV{COMSPEC} ? "$exe.exe" : $exe; | ||
} | ||
|
||
sub generate_command | ||
{ | ||
my @command = (exe('git'), 'diff'); | ||
my $skip_next = 0; | ||
my $idx = -1; | ||
for my $arg (@ARGV) { | ||
$idx++; | ||
if ($skip_next) { | ||
$skip_next = 0; | ||
next; | ||
} | ||
if ($arg eq '-t' or $arg eq '--tool') { | ||
usage() if $#ARGV <= $idx; | ||
$ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1]; | ||
$skip_next = 1; | ||
next; | ||
} | ||
if ($arg =~ /^--tool=/) { | ||
$ENV{GIT_MERGE_TOOL} = substr($arg, 7); | ||
next; | ||
} | ||
if ($arg eq '--no-prompt') { | ||
$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; | ||
next; | ||
} | ||
if ($arg eq '-h' or $arg eq '--help') { | ||
usage(); | ||
} | ||
push @command, $arg; | ||
} | ||
return @command | ||
} | ||
|
||
setup_environment(); | ||
exec(generate_command()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
#!/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 |
Oops, something went wrong.