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.
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
25 . "$(git --exec-path)/git-sh-setup"
28 . "$(git --exec-path)/git-mergetool--lib"
30 merge_tool="$(get_merge_tool)"
31 if test -z "$merge_tool"
33 echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set."
39 # needed to access tar utility
40 cdup=$(git rev-parse --show-cdup) &&
42 echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
46 # mktemp is not available on all platforms (missing from msysgit)
47 # Use a hard-coded tmp dir if it is not available
48 tmp="$(mktemp -d -t tmp.XXXXXX 2>/dev/null)" || {
49 tmp=/tmp/git-diffall-tmp.$$
50 mkdir "$tmp" || exit 1
53 trap 'rm -rf "$tmp" 2>/dev/null' EXIT
69 -h|--h|--he|--hel|--help)
78 -x|--e|--ex|--ext|--extc|--extcm|--extcmd)
81 echo You must specify the tool for use with --extcmd
92 echo Invalid option: "$1"
96 # could be commit, commit range or path limiter
108 if test -n "$dashdash_seen"
114 elif test -z "$right"
127 # Determine the set of files which changed
128 if test -n "$left" && test -n "$right"
130 left_dir="cmt-$(git rev-parse --short $left)"
131 right_dir="cmt-$(git rev-parse --short $right)"
133 if test -n "$compare_staged"
136 elif test -n "$merge_base"
138 git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist"
140 git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist"
144 left_dir="cmt-$(git rev-parse --short $left)"
146 if test -n "$compare_staged"
149 git diff --name-only --cached "$left" -- $paths >"$tmp/filelist"
151 right_dir="working_tree"
152 git diff --name-only "$left" -- $paths >"$tmp/filelist"
157 if test -n "$compare_staged"
160 git diff --name-only --cached -- $paths >"$tmp/filelist"
162 right_dir="working_tree"
163 git diff --name-only -- $paths >"$tmp/filelist"
167 # Exit immediately if there are no diffs
168 if test ! -s "$tmp/filelist"
173 if test -n "$copy_back" && test "$right_dir" != "working_tree"
175 echo "--copy-back is only valid when diff includes the working tree."
179 # Create the named tmp directories that will hold the files to be compared
180 mkdir -p "$tmp/$left_dir" "$tmp/$right_dir"
182 # Populate the tmp/right_dir directory with the files to be compared
187 ls_list=$(git ls-tree $right "$name")
188 if test -n "$ls_list"
190 mkdir -p "$tmp/$right_dir/$(dirname "$name")"
191 git show "$right":"$name" >"$tmp/$right_dir/$name" || true
193 done < "$tmp/filelist"
194 elif test -n "$compare_staged"
198 ls_list=$(git ls-files -- "$name")
199 if test -n "$ls_list"
201 mkdir -p "$tmp/$right_dir/$(dirname "$name")"
202 git show :"$name" >"$tmp/$right_dir/$name"
204 done < "$tmp/filelist"
206 # Mac users have gnutar rather than tar
207 (tar --ignore-failed-read -c -T "$tmp/filelist" | (cd "$tmp/$right_dir" && tar -x)) || {
208 gnutar --ignore-failed-read -c -T "$tmp/filelist" | (cd "$tmp/$right_dir" && gnutar -x)
212 # Populate the tmp/left_dir directory with the files to be compared
217 ls_list=$(git ls-tree $left "$name")
218 if test -n "$ls_list"
220 mkdir -p "$tmp/$left_dir/$(dirname "$name")"
221 git show "$left":"$name" >"$tmp/$left_dir/$name" || true
224 if test -n "$compare_staged"
226 ls_list=$(git ls-tree HEAD "$name")
227 if test -n "$ls_list"
229 mkdir -p "$tmp/$left_dir/$(dirname "$name")"
230 git show HEAD:"$name" >"$tmp/$left_dir/$name"
233 mkdir -p "$tmp/$left_dir/$(dirname "$name")"
234 git show :"$name" >"$tmp/$left_dir/$name"
237 done < "$tmp/filelist"
243 if test -n "$diff_tool"
246 eval $diff_tool '"$LOCAL"' '"$REMOTE"'
248 run_merge_tool "$merge_tool" false
251 # Copy files back to the working dir, if requested
252 if test -n "$copy_back" && test "$right_dir" = "working_tree"
255 git_top_dir=$(git rev-parse --show-toplevel)
256 find "$tmp/$right_dir" -type f |
259 cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}"