contrib/diffall: comment actual reason for 'cdup'
[git/gitweb.git] / contrib / diffall / git-diffall
blobd706a6dee32839007f8fcfb32794b51a1e59765a
1 #!/bin/sh
2 # Copyright 2010 - 2012, Tim Henigan <tim.henigan@gmail.com>
4 # Perform a directory diff between commits in the repository using
5 # the external diff or merge tool specified in the user's config.
7 USAGE='[--cached] [--copy-back] [-x|--extcmd=<command>] <commit>{0,2} [-- <path>*]
9 --cached Compare to the index rather than the working tree.
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.
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.
24 SUBDIRECTORY_OK=1
25 . "$(git --exec-path)/git-sh-setup"
27 TOOL_MODE=diff
28 . "$(git --exec-path)/git-mergetool--lib"
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
37 start_dir=$(pwd)
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
48 # mktemp is not available on all platforms (missing from msysgit)
49 # Use a hard-coded tmp dir if it is not available
50 tmp="$(mktemp -d -t tmp.XXXXXX 2>/dev/null)" || {
51 tmp=/tmp/git-diffall-tmp.$$
52 mkdir "$tmp" || exit 1
55 trap 'rm -rf "$tmp" 2>/dev/null' EXIT
57 left=
58 right=
59 paths=
60 dashdash_seen=
61 compare_staged=
62 merge_base=
63 left_dir=
64 right_dir=
65 diff_tool=
66 copy_back=
68 while test $# != 0
70 case "$1" in
71 -h|--h|--he|--hel|--help)
72 usage
74 --cached)
75 compare_staged=1
77 --copy-back)
78 copy_back=1
80 -x|--e|--ex|--ext|--extc|--extcm|--extcmd)
81 if test $# = 1
82 then
83 echo You must specify the tool for use with --extcmd
84 usage
85 else
86 diff_tool=$2
87 shift
90 --)
91 dashdash_seen=1
93 -*)
94 echo Invalid option: "$1"
95 usage
98 # could be commit, commit range or path limiter
99 case "$1" in
100 *...*)
101 left=${1%...*}
102 right=${1#*...}
103 merge_base=1
105 *..*)
106 left=${1%..*}
107 right=${1#*..}
110 if test -n "$dashdash_seen"
111 then
112 paths="$paths$1 "
113 elif test -z "$left"
114 then
115 left=$1
116 elif test -z "$right"
117 then
118 right=$1
119 else
120 paths="$paths$1 "
123 esac
125 esac
126 shift
127 done
129 # Determine the set of files which changed
130 if test -n "$left" && test -n "$right"
131 then
132 left_dir="cmt-$(git rev-parse --short $left)"
133 right_dir="cmt-$(git rev-parse --short $right)"
135 if test -n "$compare_staged"
136 then
137 usage
138 elif test -n "$merge_base"
139 then
140 git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist"
141 else
142 git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist"
144 elif test -n "$left"
145 then
146 left_dir="cmt-$(git rev-parse --short $left)"
148 if test -n "$compare_staged"
149 then
150 right_dir="staged"
151 git diff --name-only --cached "$left" -- $paths >"$tmp/filelist"
152 else
153 right_dir="working_tree"
154 git diff --name-only "$left" -- $paths >"$tmp/filelist"
156 else
157 left_dir="HEAD"
159 if test -n "$compare_staged"
160 then
161 right_dir="staged"
162 git diff --name-only --cached -- $paths >"$tmp/filelist"
163 else
164 right_dir="working_tree"
165 git diff --name-only -- $paths >"$tmp/filelist"
169 # Exit immediately if there are no diffs
170 if test ! -s "$tmp/filelist"
171 then
172 exit 0
175 if test -n "$copy_back" && test "$right_dir" != "working_tree"
176 then
177 echo "--copy-back is only valid when diff includes the working tree."
178 exit 1
181 # Create the named tmp directories that will hold the files to be compared
182 mkdir -p "$tmp/$left_dir" "$tmp/$right_dir"
184 # Populate the tmp/right_dir directory with the files to be compared
185 if test -n "$right"
186 then
187 while read name
189 ls_list=$(git ls-tree $right "$name")
190 if test -n "$ls_list"
191 then
192 mkdir -p "$tmp/$right_dir/$(dirname "$name")"
193 git show "$right":"$name" >"$tmp/$right_dir/$name" || true
195 done < "$tmp/filelist"
196 elif test -n "$compare_staged"
197 then
198 while read name
200 ls_list=$(git ls-files -- "$name")
201 if test -n "$ls_list"
202 then
203 mkdir -p "$tmp/$right_dir/$(dirname "$name")"
204 git show :"$name" >"$tmp/$right_dir/$name"
206 done < "$tmp/filelist"
207 else
208 # Mac users have gnutar rather than tar
209 (tar --ignore-failed-read -c -T "$tmp/filelist" | (cd "$tmp/$right_dir" && tar -x)) || {
210 gnutar --ignore-failed-read -c -T "$tmp/filelist" | (cd "$tmp/$right_dir" && gnutar -x)
214 # Populate the tmp/left_dir directory with the files to be compared
215 while read name
217 if test -n "$left"
218 then
219 ls_list=$(git ls-tree $left "$name")
220 if test -n "$ls_list"
221 then
222 mkdir -p "$tmp/$left_dir/$(dirname "$name")"
223 git show "$left":"$name" >"$tmp/$left_dir/$name" || true
225 else
226 if test -n "$compare_staged"
227 then
228 ls_list=$(git ls-tree HEAD "$name")
229 if test -n "$ls_list"
230 then
231 mkdir -p "$tmp/$left_dir/$(dirname "$name")"
232 git show HEAD:"$name" >"$tmp/$left_dir/$name"
234 else
235 mkdir -p "$tmp/$left_dir/$(dirname "$name")"
236 git show :"$name" >"$tmp/$left_dir/$name"
239 done < "$tmp/filelist"
241 cd "$tmp"
242 LOCAL="$left_dir"
243 REMOTE="$right_dir"
245 if test -n "$diff_tool"
246 then
247 export BASE
248 eval $diff_tool '"$LOCAL"' '"$REMOTE"'
249 else
250 run_merge_tool "$merge_tool" false
253 # Copy files back to the working dir, if requested
254 if test -n "$copy_back" && test "$right_dir" = "working_tree"
255 then
256 cd "$start_dir"
257 git_top_dir=$(git rev-parse --show-toplevel)
258 find "$tmp/$right_dir" -type f |
259 while read file
261 cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}"
262 done