Code

443f646a773a89041d8255d29a829fa710af03e0
[git.git] / contrib / diffall / git-diffall
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.
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.
22 '
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
35 fi
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
46 }
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" 2>/dev/null' EXIT
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=
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
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)"
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)"
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"
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
166 # Exit immediately if there are no diffs
167 if test ! -s "$tmp/filelist"
168 then
169         exit 0
170 fi
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
178 # Create the named tmp directories that will hold the files to be compared
179 mkdir -p "$tmp/$left_dir" "$tmp/$right_dir"
181 # Populate the tmp/right_dir directory with the files to be compared
182 if test -n "$right"
183 then
184         while read name
185         do
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         done < "$tmp/filelist"
193 elif test -n "$compare_staged"
194 then
195         while read name
196         do
197                 ls_list=$(git ls-files -- "$name")
198                 if test -n "$ls_list"
199                 then
200                         mkdir -p "$tmp/$right_dir/$(dirname "$name")"
201                         git show :"$name" >"$tmp/$right_dir/$name"
202                 fi
203         done < "$tmp/filelist"
204 else
205         # Mac users have gnutar rather than tar
206         (tar --ignore-failed-read -c -T "$tmp/filelist" | (cd "$tmp/$right_dir" && tar -x)) || {
207                 gnutar --ignore-failed-read -c -T "$tmp/filelist" | (cd "$tmp/$right_dir" && gnutar -x)
208         }
209 fi
211 # Populate the tmp/left_dir directory with the files to be compared
212 while read name
213 do
214         if test -n "$left"
215         then
216                 ls_list=$(git ls-tree $left "$name")
217                 if test -n "$ls_list"
218                 then
219                         mkdir -p "$tmp/$left_dir/$(dirname "$name")"
220                         git show "$left":"$name" >"$tmp/$left_dir/$name" || true
221                 fi
222         else
223                 if test -n "$compare_staged"
224                 then
225                         ls_list=$(git ls-tree HEAD "$name")
226                         if test -n "$ls_list"
227                         then
228                                 mkdir -p "$tmp/$left_dir/$(dirname "$name")"
229                                 git show HEAD:"$name" >"$tmp/$left_dir/$name"
230                         fi
231                 else
232                         mkdir -p "$tmp/$left_dir/$(dirname "$name")"
233                         git show :"$name" >"$tmp/$left_dir/$name"
234                 fi
235         fi
236 done < "$tmp/filelist"
238 cd "$tmp"
239 LOCAL="$left_dir"
240 REMOTE="$right_dir"
242 if test -n "$diff_tool"
243 then
244         export BASE
245         eval $diff_tool '"$LOCAL"' '"$REMOTE"'
246 else
247         run_merge_tool "$merge_tool" false
248 fi
250 # Copy files back to the working dir, if requested
251 if test -n "$copy_back" && test "$right_dir" = "working_tree"
252 then
253         cd "$start_dir"
254         git_top_dir=$(git rev-parse --show-toplevel)
255         find "$tmp/$right_dir" -type f |
256         while read file
257         do
258                 cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}"
259         done
260 fi