@matheusbsbs / matheus-bagliano / commits / 5c38ea31f3
Fork 0

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>

David Aguilar committed Jan 16, 2009 at 00:00 UTC 5c38ea31f345d08f37685cf4f50c599a7af56bcf
3 files changed +418
contrib/difftool/git-difftool new
+74
@@ -0,0 +1,74 @@
1 +#!/usr/bin/env perl
2 +# Copyright (c) 2009 David Aguilar
3 +#
4 +# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
5 +# git-difftool-helper script. This script exports
6 +# GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and
7 +# GIT_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper.
8 +# Any arguments that are unknown to this script are forwarded to 'git diff'.
9 +
10 +use strict;
11 +use warnings;
12 +use Cwd qw(abs_path);
13 +use File::Basename qw(dirname);
14 +
15 +my $DIR = abs_path(dirname($0));
16 +
17 +
18 +sub usage
19 +{
20 + print << 'USAGE';
21 +
22 +usage: git difftool [--no-prompt] [--tool=tool] ["git diff" options]
23 +USAGE
24 + exit 1;
25 +}
26 +
27 +sub setup_environment
28 +{
29 + $ENV{PATH} = "$DIR:$ENV{PATH}";
30 + $ENV{GIT_PAGER} = '';
31 + $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper';
32 +}
33 +
34 +sub exe
35 +{
36 + my $exe = shift;
37 + return defined $ENV{COMSPEC} ? "$exe.exe" : $exe;
38 +}
39 +
40 +sub generate_command
41 +{
42 + my @command = (exe('git'), 'diff');
43 + my $skip_next = 0;
44 + my $idx = -1;
45 + for my $arg (@ARGV) {
46 + $idx++;
47 + if ($skip_next) {
48 + $skip_next = 0;
49 + next;
50 + }
51 + if ($arg eq '-t' or $arg eq '--tool') {
52 + usage() if $#ARGV <= $idx;
53 + $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1];
54 + $skip_next = 1;
55 + next;
56 + }
57 + if ($arg =~ /^--tool=/) {
58 + $ENV{GIT_MERGE_TOOL} = substr($arg, 7);
59 + next;
60 + }
61 + if ($arg eq '--no-prompt') {
62 + $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
63 + next;
64 + }
65 + if ($arg eq '-h' or $arg eq '--help') {
66 + usage();
67 + }
68 + push @command, $arg;
69 + }
70 + return @command
71 +}
72 +
73 +setup_environment();
74 +exec(generate_command());
contrib/difftool/git-difftool-helper new
+240
@@ -0,0 +1,240 @@
1 +#!/bin/sh
2 +# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
3 +# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge,
4 +# vimdiff, gvimdiff, and custom user-configurable tools.
5 +# This script is typically launched by using the 'git difftool'
6 +# convenience command.
7 +#
8 +# Copyright (c) 2009 David Aguilar
9 +
10 +# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
11 +should_prompt () {
12 + ! test -n "$GIT_DIFFTOOL_NO_PROMPT"
13 +}
14 +
15 +# Should we keep the backup .orig file?
16 +keep_backup_mode="$(git config --bool merge.keepBackup || echo true)"
17 +keep_backup () {
18 + test "$keep_backup_mode" = "true"
19 +}
20 +
21 +# This function manages the backup .orig file.
22 +# A backup $MERGED.orig file is created if changes are detected.
23 +cleanup_temp_files () {
24 + if test -n "$MERGED"; then
25 + if keep_backup && test "$MERGED" -nt "$BACKUP"; then
26 + test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
27 + else
28 + rm -f -- "$BACKUP"
29 + fi
30 + fi
31 +}
32 +
33 +# This is called when users Ctrl-C out of git-difftool-helper
34 +sigint_handler () {
35 + echo
36 + cleanup_temp_files
37 + exit 1
38 +}
39 +
40 +# This function prepares temporary files and launches the appropriate
41 +# merge tool.
42 +launch_merge_tool () {
43 + # Merged is the filename as it appears in the work tree
44 + # Local is the contents of a/filename
45 + # Remote is the contents of b/filename
46 + # Custom merge tool commands might use $BASE so we provide it
47 + MERGED="$1"
48 + LOCAL="$2"
49 + REMOTE="$3"
50 + BASE="$1"
51 + ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
52 + BACKUP="$MERGED.BACKUP.$ext"
53 +
54 + # Create and ensure that we clean up $BACKUP
55 + test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
56 + trap sigint_handler SIGINT
57 +
58 + # $LOCAL and $REMOTE are temporary files so prompt
59 + # the user with the real $MERGED name before launching $merge_tool.
60 + if should_prompt; then
61 + printf "\nViewing: '$MERGED'\n"
62 + printf "Hit return to launch '%s': " "$merge_tool"
63 + read ans
64 + fi
65 +
66 + # Run the appropriate merge tool command
67 + case "$merge_tool" in
68 + kdiff3)
69 + basename=$(basename "$MERGED")
70 + "$merge_tool_path" --auto \
71 + --L1 "$basename (A)" \
72 + --L2 "$basename (B)" \
73 + -o "$MERGED" "$LOCAL" "$REMOTE" \
74 + > /dev/null 2>&1
75 + ;;
76 +
77 + tkdiff)
78 + "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
79 + ;;
80 +
81 + meld|vimdiff)
82 + "$merge_tool_path" "$LOCAL" "$REMOTE"
83 + ;;
84 +
85 + gvimdiff)
86 + "$merge_tool_path" -f "$LOCAL" "$REMOTE"
87 + ;;
88 +
89 + xxdiff)
90 + "$merge_tool_path" \
91 + -X \
92 + -R 'Accel.SaveAsMerged: "Ctrl-S"' \
93 + -R 'Accel.Search: "Ctrl+F"' \
94 + -R 'Accel.SearchForward: "Ctrl-G"' \
95 + --merged-file "$MERGED" \
96 + "$LOCAL" "$REMOTE"
97 + ;;
98 +
99 + opendiff)
100 + "$merge_tool_path" "$LOCAL" "$REMOTE" \
101 + -merge "$MERGED" | cat
102 + ;;
103 +
104 + ecmerge)
105 + "$merge_tool_path" "$LOCAL" "$REMOTE" \
106 + --default --mode=merge2 --to="$MERGED"
107 + ;;
108 +
109 + emerge)
110 + "$merge_tool_path" -f emerge-files-command \
111 + "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
112 + ;;
113 +
114 + *)
115 + if test -n "$merge_tool_cmd"; then
116 + ( eval $merge_tool_cmd )
117 + fi
118 + ;;
119 + esac
120 +
121 + cleanup_temp_files
122 +}
123 +
124 +# Verifies that mergetool.<tool>.cmd exists
125 +valid_custom_tool() {
126 + merge_tool_cmd="$(git config mergetool.$1.cmd)"
127 + test -n "$merge_tool_cmd"
128 +}
129 +
130 +# Verifies that the chosen merge tool is properly setup.
131 +# Built-in merge tools are always valid.
132 +valid_tool() {
133 + case "$1" in
134 + kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
135 + ;; # happy
136 + *)
137 + if ! valid_custom_tool "$1"
138 + then
139 + return 1
140 + fi
141 + ;;
142 + esac
143 +}
144 +
145 +# Sets up the merge_tool_path variable.
146 +# This handles the mergetool.<tool>.path configuration.
147 +init_merge_tool_path() {
148 + merge_tool_path=$(git config mergetool."$1".path)
149 + if test -z "$merge_tool_path"; then
150 + case "$1" in
151 + emerge)
152 + merge_tool_path=emacs
153 + ;;
154 + *)
155 + merge_tool_path="$1"
156 + ;;
157 + esac
158 + fi
159 +}
160 +
161 +# Allow the GIT_MERGE_TOOL variable to provide a default value
162 +test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
163 +
164 +# If not merge tool was specified then use the merge.tool
165 +# configuration variable. If that's invalid then reset merge_tool.
166 +if test -z "$merge_tool"; then
167 + merge_tool=$(git config merge.tool)
168 + if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
169 + echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
170 + echo >&2 "Resetting to default..."
171 + unset merge_tool
172 + fi
173 +fi
174 +
175 +# Try to guess an appropriate merge tool if no tool has been set.
176 +if test -z "$merge_tool"; then
177 +
178 + # We have a $DISPLAY so try some common UNIX merge tools
179 + if test -n "$DISPLAY"; then
180 + merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
181 + # If gnome then prefer meld
182 + if test -n "$GNOME_DESKTOP_SESSION_ID"; then
183 + merge_tool_candidates="meld $merge_tool_candidates"
184 + fi
185 + # If KDE then prefer kdiff3
186 + if test "$KDE_FULL_SESSION" = "true"; then
187 + merge_tool_candidates="kdiff3 $merge_tool_candidates"
188 + fi
189 + fi
190 +
191 + # $EDITOR is emacs so add emerge as a candidate
192 + if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
193 + merge_tool_candidates="$merge_tool_candidates emerge"
194 + fi
195 +
196 + # $EDITOR is vim so add vimdiff as a candidate
197 + if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
198 + merge_tool_candidates="$merge_tool_candidates vimdiff"
199 + fi
200 +
201 + merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
202 + echo "merge tool candidates: $merge_tool_candidates"
203 +
204 + # Loop over each candidate and stop when a valid merge tool is found.
205 + for i in $merge_tool_candidates
206 + do
207 + init_merge_tool_path $i
208 + if type "$merge_tool_path" > /dev/null 2>&1; then
209 + merge_tool=$i
210 + break
211 + fi
212 + done
213 +
214 + if test -z "$merge_tool" ; then
215 + echo "No known merge resolution program available."
216 + exit 1
217 + fi
218 +
219 +else
220 + # A merge tool has been set, so verify that it's valid.
221 + if ! valid_tool "$merge_tool"; then
222 + echo >&2 "Unknown merge tool $merge_tool"
223 + exit 1
224 + fi
225 +
226 + init_merge_tool_path "$merge_tool"
227 +
228 + if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
229 + echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
230 + exit 1
231 + fi
232 +fi
233 +
234 +
235 +# Launch the merge tool on each path provided by 'git diff'
236 +while test $# -gt 6
237 +do
238 + launch_merge_tool "$1" "$2" "$5"
239 + shift 7
240 +done
contrib/difftool/git-difftool.txt new
+104
@@ -0,0 +1,104 @@
1 +git-difftool(1)
2 +===============
3 +
4 +NAME
5 +----
6 +git-difftool - compare changes using common merge tools
7 +
8 +SYNOPSIS
9 +--------
10 +'git difftool' [--tool=<tool>] [--no-prompt] ['git diff' options]
11 +
12 +DESCRIPTION
13 +-----------
14 +'git difftool' is a git command that allows you to compare and edit files
15 +between revisions using common merge tools. At its most basic level,
16 +'git difftool' does what 'git mergetool' does but its use is for non-merge
17 +situations such as when preparing commits or comparing changes against
18 +the index.
19 +
20 +'git difftool' is a frontend to 'git diff' and accepts the same
21 +arguments and options.
22 +
23 +See linkgit:git-diff[7] for the full list of supported options.
24 +
25 +OPTIONS
26 +-------
27 +-t <tool>::
28 +--tool=<tool>::
29 + Use the merge resolution program specified by <tool>.
30 + Valid merge tools are:
31 + kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
32 ++
33 +If a merge resolution program is not specified, 'git difftool'
34 +will use the configuration variable `merge.tool`. If the
35 +configuration variable `merge.tool` is not set, 'git difftool'
36 +will pick a suitable default.
37 ++
38 +You can explicitly provide a full path to the tool by setting the
39 +configuration variable `mergetool.<tool>.path`. For example, you
40 +can configure the absolute path to kdiff3 by setting
41 +`mergetool.kdiff3.path`. Otherwise, 'git difftool' assumes the
42 +tool is available in PATH.
43 ++
44 +Instead of running one of the known merge tool programs,
45 +'git difftool' can be customized to run an alternative program
46 +by specifying the command line to invoke in a configuration
47 +variable `mergetool.<tool>.cmd`.
48 ++
49 +When 'git difftool' is invoked with this tool (either through the
50 +`-t` or `--tool` option or the `merge.tool` configuration variable)
51 +the configured command line will be invoked with the following
52 +variables available: `$LOCAL` is set to the name of the temporary
53 +file containing the contents of the diff pre-image and `$REMOTE`
54 +is set to the name of the temporary file containing the contents
55 +of the diff post-image. `$BASE` is provided for compatibility
56 +with custom merge tool commands and has the same value as `$LOCAL`.
57 +
58 +--no-prompt::
59 + Do not prompt before launching a merge tool.
60 +
61 +CONFIG VARIABLES
62 +----------------
63 +merge.tool::
64 + The default merge tool to use.
65 ++
66 +See the `--tool=<tool>` option above for more details.
67 +
68 +merge.keepBackup::
69 + The original, unedited file content can be saved to a file with
70 + a `.orig` extension. Defaults to `true` (i.e. keep the backup files).
71 +
72 +mergetool.<tool>.path::
73 + Override the path for the given tool. This is useful in case
74 + your tool is not in the PATH.
75 +
76 +mergetool.<tool>.cmd::
77 + Specify the command to invoke the specified merge tool.
78 ++
79 +See the `--tool=<tool>` option above for more details.
80 +
81 +
82 +SEE ALSO
83 +--------
84 +linkgit:git-diff[7]::
85 + Show changes between commits, commit and working tree, etc
86 +
87 +linkgit:git-mergetool[1]::
88 + Run merge conflict resolution tools to resolve merge conflicts
89 +
90 +linkgit:git-config[7]::
91 + Get and set repository or global options
92 +
93 +
94 +AUTHOR
95 +------
96 +Written by David Aguilar <davvid@gmail.com>.
97 +
98 +Documentation
99 +--------------
100 +Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
101 +
102 +GIT
103 +---
104 +Part of the linkgit:git[1] suite