From: "David Aguilar" <davvid@gmail.com>
To: "Matthieu Moy" <Matthieu.Moy@imag.fr>
Cc: markus.heidelberg@web.de, git@vger.kernel.org
Subject: Re: git-difftool
Date: Fri, 2 Jan 2009 09:39:34 -0800 [thread overview]
Message-ID: <402731c90901020939k1a8ae795oc4cbfd0ced992aab@mail.gmail.com> (raw)
In-Reply-To: <vpq63kxofi3.fsf@bauges.imag.fr>
[-- Attachment #1: Type: text/plain, Size: 1519 bytes --]
On Fri, Jan 2, 2009 at 8:10 AM, Matthieu Moy <Matthieu.Moy@imag.fr> wrote:
>
> As done with "vimdiff" in another message, simply write a
> one-liner wrapper script that calls xxdiff $2 $3, and call this
> wrapper script.
>
> ...
>
> Right, but a script "git-difftool" calling the later is a one-liner,
> so 2 one-liners give you the same result as the ~500 lines script
> proposed. And GIT_EXTERNAL_DIFF has the great advantage
> of being maintained together with git, and will most likely handle
> all cases (diff between index, working tree, arbitrary commit, ...)
> correctly.
>
> --
> Matthieu
>
Thanks for the feedback. I've done exactly what you suggested.
I now have a git-difftool wrapper script that basically just sets up
the environment for git-difftool-helper. git-difftool-helper does all
of the merge tool configuration stuff ala
http://www.kernel.org/pub/software/scm/git/docs/git-mergetool.html (it
uses the same git config variables and thus works with existing custom
commands). If you drop them both into the same directory it should
work as-is (it munges $PATH).
It's not a two-liner (they do all that git config stuff and handle
more than just vimdiff) but it does use GIT_EXTERNAL_DIFF now, which
makes the script infinitely more useful. This is much nicer now since
you can pass any 'git diff' options to git-difftool and it'll handle
it correctly.
The usage is simpler now too:
usage: git difftool [--no-prompt] [--tool=tool] ["git diff" options]
Thanks for your help,
--
David
[-- Attachment #2: git-difftool --]
[-- Type: application/octet-stream, Size: 1876 bytes --]
#!/usr/bin/env perl
# This is a wrapper around the GIT_EXTERNAL_DIFF compatible
# git-difftool-helper script.
#
# Copyright (c) 2009 David Aguilar
use strict;
use warnings;
use File::Basename qw(dirname);
# Prints usage and exits
sub usage
{
print << 'USAGE';
usage: git difftool [--no-prompt] [--tool=tool] ["git diff" options]
USAGE
exit 1;
}
# Sets PATH, GIT_PAGER, and GIT_EXTERNAL_DIFF so that
# git-difftool-helper is invoked by 'git diff'.
sub setup_environment
{
my @paths = ( dirname($0), split(':', $ENV{PATH}) );
$ENV{PATH} = join(':', @paths);
$ENV{GIT_PAGER} = '';
$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper';
}
# Sets an environment variable recognized by git-difftool-helper
# to signal that we shouldn't prompt before viewing each file.
sub no_prompt
{
$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
}
# Sets a default value for the merge tool used by git-difftool-helper.
sub setup_mergetool
{
$ENV{GIT_MERGE_TOOL} = shift;
}
# Processes command-line flags and generates a corresponding
# 'git diff' command. Any arguments that are unknown to this
# script are assumed to be arguments intended for 'git diff'.
sub generate_command
{
my @command = ('git', 'diff');
my $skip_next = 0;
my $idx = -1;
for my $arg (@ARGV) {
$idx++;
if ($skip_next) {
$skip_next = 0;
next;
}
if ($arg eq '-h' or $arg eq '--help') {
usage()
}
if ($arg eq '--no-prompt') {
no_prompt();
next;
}
if ($arg eq '-t' or $arg eq '--tool') {
$skip_next = 1;
if ( scalar(@ARGV) <= $idx + 1 ) {
usage();
}
setup_mergetool($ARGV[$idx + 1]);
next;
}
if ($arg =~ /^--tool=/) {
$arg = substr($arg, 7);
setup_mergetool($arg);
next;
}
push @command, $arg;
}
return @command
}
# Sets up the git-difftool environment and execs 'git diff'.
sub main
{
setup_environment();
exec(generate_command());
}
main()
[-- Attachment #3: git-difftool-helper --]
[-- Type: application/octet-stream, Size: 5979 bytes --]
#!/bin/sh
#
# git-difftool-helper:
# invoked via 'git difftool' to view diffs using external diff viewers.
#
# Copyright (c) 2009 David Aguilar.
#
# You can use this script directly with the following settings:
# export GIT_PAGER=''
# export GIT_EXTERNAL_DIFF=git-difftool-helper
# git diff ...
# Should we prompt before viewing each file?
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 helper on each path passed to us by 'git diff'
while test $# -gt 6
do
launch_merge_tool "$1" "$2" "$5"
shift 7
done
next prev parent reply other threads:[~2009-01-02 17:41 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-12-26 1:30 git-difftool David Aguilar
2008-12-31 16:04 ` git-difftool Matthieu Moy
2008-12-31 20:11 ` git-difftool David Aguilar
2008-12-31 21:20 ` git-difftool Boyd Stephen Smith Jr.
2009-01-01 17:58 ` git-difftool Matthieu Moy
2009-01-02 0:13 ` git-difftool Markus Heidelberg
2009-01-02 16:10 ` git-difftool Matthieu Moy
2009-01-02 17:39 ` David Aguilar [this message]
2009-01-15 22:26 ` git-difftool Markus Heidelberg
2009-01-16 5:05 ` git-difftool David Aguilar
2009-01-01 7:38 ` git-difftool Ping Yin
2009-01-02 0:04 ` git-difftool Markus Heidelberg
2009-01-02 1:59 ` git-difftool Ping Yin
2009-01-17 3:47 ` git-difftool enso
2009-01-17 4:41 ` git-difftool David Aguilar
2009-01-17 14:17 ` git-difftool Ping Yin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=402731c90901020939k1a8ae795oc4cbfd0ced992aab@mail.gmail.com \
--to=davvid@gmail.com \
--cc=Matthieu.Moy@imag.fr \
--cc=git@vger.kernel.org \
--cc=markus.heidelberg@web.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).