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