1
-#!/bin/sh
2
-# Copyright 2010 - 2012, Tim Henigan <tim.henigan@gmail.com>
3
-#
4
-# Perform a directory diff between commits in the repository using
5
-# the external diff or merge tool specified in the user's config.
6
-
7
-USAGE='[--cached] [--copy-back] [-x|--extcmd=<command>] <commit>{0,2} [-- <path>*]
8
-
9
- --cached Compare to the index rather than the working tree.
10
-
11
- --copy-back Copy files back to the working tree when the diff
12
- tool exits (in case they were modified by the
13
- user). This option is only valid if the diff
14
- compared with the working tree.
15
-
16
- -x=<command>
17
- --extcmd=<command> Specify a custom command for viewing diffs.
18
- git-diffall ignores the configured defaults and
19
- runs $command $LOCAL $REMOTE when this option is
20
- specified. Additionally, $BASE is set in the
21
- environment.
22
-'
23
-
24
-SUBDIRECTORY_OK=1
25
-. "$(git --exec-path)/git-sh-setup"
26
-
27
-TOOL_MODE=diff
28
-. "$(git --exec-path)/git-mergetool--lib"
29
-
30
-merge_tool="$(get_merge_tool)"
31
-if test -z "$merge_tool"
32
-then
33
- echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set."
34
- usage
35
-fi
36
-
37
-start_dir=$(pwd)
38
-
39
-# All the file paths returned by the diff command are relative to the root
40
-# of the working copy. So if the script is called from a subdirectory, it
41
-# must switch to the root of working copy before trying to use those paths.
42
-cdup=$(git rev-parse --show-cdup) &&
43
-cd "$cdup" || {
44
- echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
45
- exit 1
46
-}
47
-
48
-# set up temp dir
49
-tmp=$(perl -e 'use File::Temp qw(tempdir);
50
- $t=tempdir("/tmp/git-diffall.XXXXX") or exit(1);
51
- print $t') || exit 1
52
-trap 'rm -rf "$tmp"' EXIT
53
-
54
-left=
55
-right=
56
-paths=
57
-dashdash_seen=
58
-compare_staged=
59
-merge_base=
60
-left_dir=
61
-right_dir=
62
-diff_tool=
63
-copy_back=
64
-
65
-while test $# != 0
66
-do
67
- case "$1" in
68
- -h|--h|--he|--hel|--help)
69
- usage
70
- ;;
71
- --cached)
72
- compare_staged=1
73
- ;;
74
- --copy-back)
75
- copy_back=1
76
- ;;
77
- -x|--e|--ex|--ext|--extc|--extcm|--extcmd)
78
- if test $# = 1
79
- then
80
- echo You must specify the tool for use with --extcmd
81
- usage
82
- else
83
- diff_tool=$2
84
- shift
85
- fi
86
- ;;
87
- --)
88
- dashdash_seen=1
89
- ;;
90
- -*)
91
- echo Invalid option: "$1"
92
- usage
93
- ;;
94
- *)
95
- # could be commit, commit range or path limiter
96
- case "$1" in
97
- *...*)
98
- left=${1%...*}
99
- right=${1#*...}
100
- merge_base=1
101
- ;;
102
- *..*)
103
- left=${1%..*}
104
- right=${1#*..}
105
- ;;
106
- *)
107
- if test -n "$dashdash_seen"
108
- then
109
- paths="$paths$1 "
110
- elif test -z "$left"
111
- then
112
- left=$1
113
- elif test -z "$right"
114
- then
115
- right=$1
116
- else
117
- paths="$paths$1 "
118
- fi
119
- ;;
120
- esac
121
- ;;
122
- esac
123
- shift
124
-done
125
-
126
-# Determine the set of files which changed
127
-if test -n "$left" && test -n "$right"
128
-then
129
- left_dir="cmt-$(git rev-parse --short $left)"
130
- right_dir="cmt-$(git rev-parse --short $right)"
131
-
132
- if test -n "$compare_staged"
133
- then
134
- usage
135
- elif test -n "$merge_base"
136
- then
137
- git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist"
138
- else
139
- git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist"
140
- fi
141
-elif test -n "$left"
142
-then
143
- left_dir="cmt-$(git rev-parse --short $left)"
144
-
145
- if test -n "$compare_staged"
146
- then
147
- right_dir="staged"
148
- git diff --name-only --cached "$left" -- $paths >"$tmp/filelist"
149
- else
150
- right_dir="working_tree"
151
- git diff --name-only "$left" -- $paths >"$tmp/filelist"
152
- fi
153
-else
154
- left_dir="HEAD"
155
-
156
- if test -n "$compare_staged"
157
- then
158
- right_dir="staged"
159
- git diff --name-only --cached -- $paths >"$tmp/filelist"
160
- else
161
- right_dir="working_tree"
162
- git diff --name-only -- $paths >"$tmp/filelist"
163
- fi
164
-fi
165
-
166
-# Exit immediately if there are no diffs
167
-if test ! -s "$tmp/filelist"
168
-then
169
- exit 0
170
-fi
171
-
172
-if test -n "$copy_back" && test "$right_dir" != "working_tree"
173
-then
174
- echo "--copy-back is only valid when diff includes the working tree."
175
- exit 1
176
-fi
177
-
178
-# Create the named tmp directories that will hold the files to be compared
179
-mkdir -p "$tmp/$left_dir" "$tmp/$right_dir"
180
-
181
-# Populate the tmp/right_dir directory with the files to be compared
182
-while read name
183
-do
184
- if test -n "$right"
185
- then
186
- ls_list=$(git ls-tree $right "$name")
187
- if test -n "$ls_list"
188
- then
189
- mkdir -p "$tmp/$right_dir/$(dirname "$name")"
190
- git show "$right":"$name" >"$tmp/$right_dir/$name" || true
191
- fi
192
- elif test -n "$compare_staged"
193
- then
194
- ls_list=$(git ls-files -- "$name")
195
- if test -n "$ls_list"
196
- then
197
- mkdir -p "$tmp/$right_dir/$(dirname "$name")"
198
- git show :"$name" >"$tmp/$right_dir/$name"
199
- fi
200
- else
201
- if test -e "$name"
202
- then
203
- mkdir -p "$tmp/$right_dir/$(dirname "$name")"
204
- cp "$name" "$tmp/$right_dir/$name"
205
- fi
206
- fi
207
-done < "$tmp/filelist"
208
-
209
-# Populate the tmp/left_dir directory with the files to be compared
210
-while read name
211
-do
212
- if test -n "$left"
213
- then
214
- ls_list=$(git ls-tree $left "$name")
215
- if test -n "$ls_list"
216
- then
217
- mkdir -p "$tmp/$left_dir/$(dirname "$name")"
218
- git show "$left":"$name" >"$tmp/$left_dir/$name" || true
219
- fi
220
- else
221
- if test -n "$compare_staged"
222
- then
223
- ls_list=$(git ls-tree HEAD "$name")
224
- if test -n "$ls_list"
225
- then
226
- mkdir -p "$tmp/$left_dir/$(dirname "$name")"
227
- git show HEAD:"$name" >"$tmp/$left_dir/$name"
228
- fi
229
- else
230
- mkdir -p "$tmp/$left_dir/$(dirname "$name")"
231
- git show :"$name" >"$tmp/$left_dir/$name"
232
- fi
233
- fi
234
-done < "$tmp/filelist"
235
-
236
-LOCAL="$tmp/$left_dir"
237
-REMOTE="$tmp/$right_dir"
238
-
239
-if test -n "$diff_tool"
240
-then
241
- export BASE
242
- eval $diff_tool '"$LOCAL"' '"$REMOTE"'
243
-else
244
- run_merge_tool "$merge_tool" false
245
-fi
246
-
247
-# Copy files back to the working dir, if requested
248
-if test -n "$copy_back" && test "$right_dir" = "working_tree"
249
-then
250
- cd "$start_dir"
251
- git_top_dir=$(git rev-parse --show-toplevel)
252
- find "$tmp/$right_dir" -type f |
253
- while read file
254
- do
255
- cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}"
256
- done
257
-fi