From: David Aguilar <davvid@gmail.com>
To: git@vger.kernel.org
Cc: gitster@pobox.com, markus.heidelberg@web.de,
David Aguilar <davvid@gmail.com>
Subject: [PATCH v2] contrib: add 'git-difftool' for launching common diff tools
Date: Sun, 18 Jan 2009 16:25:53 -0800 [thread overview]
Message-ID: <1232324753-16137-1-git-send-email-davvid@gmail.com> (raw)
In-Reply-To: <200901182025.24045.markus.heidelberg@web.de>
'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>
---
This newer version addresses the feedback from Markus Heidelberg.
difftool is now more consistent with mergetool and it includes
Markus's vimdiff enhancements for positioning the cursor at startup.
contrib/difftool/git-difftool | 73 ++++++++++
contrib/difftool/git-difftool-helper | 244 ++++++++++++++++++++++++++++++++++
contrib/difftool/git-difftool.txt | 104 ++++++++++++++
3 files changed, 421 insertions(+), 0 deletions(-)
create mode 100755 contrib/difftool/git-difftool
create mode 100755 contrib/difftool/git-difftool-helper
create mode 100644 contrib/difftool/git-difftool.txt
diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool
new file mode 100755
index 0000000..0cda3d2
--- /dev/null
+++ b/contrib/difftool/git-difftool
@@ -0,0 +1,73 @@
+#!/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_DIFFTOOL_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 [--tool=<tool>] [--no-prompt] ["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());
diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper
new file mode 100755
index 0000000..a6f862f
--- /dev/null
+++ b/contrib/difftool/git-difftool-helper
@@ -0,0 +1,244 @@
+#!/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)
+ "$merge_tool_path" "$LOCAL" "$REMOTE"
+ ;;
+
+ vimdiff)
+ "$merge_tool_path" -c "wincmd r" "$LOCAL" "$REMOTE"
+ ;;
+
+ gvimdiff)
+ "$merge_tool_path" -c "wincmd r" -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
diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt
new file mode 100644
index 0000000..ca3dbd2
--- /dev/null
+++ b/contrib/difftool/git-difftool.txt
@@ -0,0 +1,104 @@
+git-difftool(1)
+===============
+
+NAME
+----
+git-difftool - compare changes using common merge tools
+
+SYNOPSIS
+--------
+'git difftool' [--tool=<tool>] [--no-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.
+
+OPTIONS
+-------
+-t <tool>::
+--tool=<tool>::
+ Use the merge resolution program specified by <tool>.
+ Valid merge tools are:
+ kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
++
+If a merge resolution program is not specified, 'git-difftool'
+will use the configuration variable `merge.tool`. If the
+configuration variable `merge.tool` is not set, 'git difftool'
+will pick a suitable default.
++
+You can explicitly provide a full path to the tool by setting the
+configuration variable `mergetool.<tool>.path`. For example, you
+can configure the absolute path to kdiff3 by setting
+`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
+tool is available in PATH.
++
+Instead of running one of the known merge tool programs,
+'git-difftool' can be customized to run an alternative program
+by specifying the command line to invoke in a configuration
+variable `mergetool.<tool>.cmd`.
++
+When 'git-difftool' is invoked with this tool (either through the
+`-t` or `--tool` option or the `merge.tool` configuration variable)
+the configured command line will be invoked 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. `$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.
+
+CONFIG VARIABLES
+----------------
+merge.tool::
+ The default merge tool to use.
++
+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).
+
+mergetool.<tool>.path::
+ Override the path for the given tool. This is useful in case
+ your tool is not in the PATH.
+
+mergetool.<tool>.cmd::
+ Specify the command to invoke the specified merge tool.
++
+See the `--tool=<tool>` option above for more details.
+
+
+SEE ALSO
+--------
+linkgit:git-diff[1]::
+ Show changes between commits, commit and working tree, etc
+
+linkgit:git-mergetool[1]::
+ Run merge conflict resolution tools to resolve merge conflicts
+
+linkgit:git-config[1]::
+ Get and set repository or global options
+
+
+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
--
1.6.1.149.g7bbd8
next prev parent reply other threads:[~2009-01-19 0:26 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-01-16 8:00 [PATCH] contrib: add 'git difftool' for launching common merge tools David Aguilar
2009-01-18 19:25 ` Markus Heidelberg
2009-01-19 0:25 ` David Aguilar [this message]
2009-01-19 4:45 ` [PATCH v2] contrib: add 'git-difftool' for launching common diff tools Markus Heidelberg
2009-01-19 0:34 ` [PATCH] contrib: add 'git difftool' for launching common merge tools David Aguilar
2009-01-19 5:03 ` Markus Heidelberg
2009-01-19 5:32 ` David Aguilar
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1232324753-16137-1-git-send-email-davvid@gmail.com \
--to=davvid@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--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.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.