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 7USAGE='[--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$REMOTEwhen this option is 20 specified. Additionally,$BASEis set in the 21 environment. 22' 23 24SUBDIRECTORY_OK=1 25. "$(git --exec-path)/git-sh-setup" 26 27TOOL_MODE=diff 28. "$(git --exec-path)/git-mergetool--lib" 29 30merge_tool="$(get_merge_tool)" 31iftest -z"$merge_tool" 32then 33echo"Error: Either the 'diff.tool' or 'merge.tool' option must be set." 34 usage 35fi 36 37start_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. 42cdup=$(git rev-parse --show-cdup)&& 43cd"$cdup"|| { 44echo>&2"Cannot chdir to$cdup, the toplevel of the working tree" 45exit1 46} 47 48# set up temp dir 49tmp=$(perl -e 'use File::Temp qw(tempdir); 50$t=tempdir("/tmp/git-diffall.XXXXX") or exit(1); 51 print $t') || exit 1 52trap 'rm-rf"$tmp"2>/dev/null' EXIT 53 54left= 55right= 56paths= 57dashdash_seen= 58compare_staged= 59merge_base= 60left_dir= 61right_dir= 62diff_tool= 63copy_back= 64 65while test$#!= 0 66do 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 124done 125 126# Determine the set of files which changed 127if test -n "$left" && test -n "$right" 128then 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 141elif test -n "$left" 142then 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 153else 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 164fi 165 166# Exit immediately if there are no diffs 167if test ! -s "$tmp/filelist" 168then 169 exit 0 170fi 171 172if test -n "$copy_back" && test "$right_dir" != "working_tree" 173then 174 echo "--copy-back is only valid when diff includes the working tree." 175 exit 1 176fi 177 178# Create the named tmp directories that will hold the files to be compared 179mkdir -p "$tmp/$left_dir" "$tmp/$right_dir" 180 181# Populate the tmp/right_dir directory with the files to be compared 182if test -n "$right" 183then 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" 193elif test -n "$compare_staged" 194then 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" 204else 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 } 209fi 210 211# Populate the tmp/left_dir directory with the files to be compared 212while read name 213do 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 236done < "$tmp/filelist" 237 238cd "$tmp" 239LOCAL="$left_dir" 240REMOTE="$right_dir" 241 242if test -n "$diff_tool" 243then 244 export BASE 245 eval$diff_tool'"$LOCAL"' '"$REMOTE"' 246else 247 run_merge_tool "$merge_tool" false 248fi 249 250# Copy files back to the working dir, if requested 251if test -n "$copy_back" && test "$right_dir" = "working_tree" 252then 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 260fi