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# needed to access tar utility 40cdup=$(git rev-parse --show-cdup)&& 41cd"$cdup"|| { 42echo>&2"Cannot chdir to$cdup, the toplevel of the working tree" 43exit1 44} 45 46# mktemp is not available on all platforms (missing from msysgit) 47# Use a hard-coded tmp dir if it is not available 48tmp="$(mktemp -d -t tmp.XXXXXX 2>/dev/null)"|| { 49 tmp=/tmp/git-diffall-tmp.$$ 50mkdir"$tmp"||exit1 51} 52 53trap'rm -rf "$tmp" 2>/dev/null' EXIT 54 55left= 56right= 57paths= 58dashdash_seen= 59compare_staged= 60merge_base= 61left_dir= 62right_dir= 63diff_tool= 64copy_back= 65 66whiletest$#!=0 67do 68case"$1"in 69-h|--h|--he|--hel|--help) 70 usage 71;; 72--cached) 73 compare_staged=1 74;; 75--copy-back) 76 copy_back=1 77;; 78-x|--e|--ex|--ext|--extc|--extcm|--extcmd) 79iftest$#=1 80then 81echo You must specify the tool for use with --extcmd 82 usage 83else 84 diff_tool=$2 85shift 86fi 87;; 88--) 89 dashdash_seen=1 90;; 91-*) 92echo Invalid option:"$1" 93 usage 94;; 95*) 96# could be commit, commit range or path limiter 97case"$1"in 98*...*) 99 left=${1%...*} 100 right=${1#*...} 101 merge_base=1 102;; 103*..*) 104 left=${1%..*} 105 right=${1#*..} 106;; 107*) 108iftest -n"$dashdash_seen" 109then 110 paths="$paths$1" 111eliftest -z"$left" 112then 113 left=$1 114eliftest -z"$right" 115then 116 right=$1 117else 118 paths="$paths$1" 119fi 120;; 121esac 122;; 123esac 124shift 125done 126 127# Determine the set of files which changed 128iftest -n"$left"&&test -n"$right" 129then 130 left_dir="cmt-$(git rev-parse --short $left)" 131 right_dir="cmt-$(git rev-parse --short $right)" 132 133iftest -n"$compare_staged" 134then 135 usage 136eliftest -n"$merge_base" 137then 138 git diff--name-only"$left"..."$right"--$paths>"$tmp/filelist" 139else 140 git diff--name-only"$left""$right"--$paths>"$tmp/filelist" 141fi 142eliftest -n"$left" 143then 144 left_dir="cmt-$(git rev-parse --short $left)" 145 146iftest -n"$compare_staged" 147then 148 right_dir="staged" 149 git diff--name-only --cached"$left"--$paths>"$tmp/filelist" 150else 151 right_dir="working_tree" 152 git diff--name-only"$left"--$paths>"$tmp/filelist" 153fi 154else 155 left_dir="HEAD" 156 157iftest -n"$compare_staged" 158then 159 right_dir="staged" 160 git diff--name-only --cached --$paths>"$tmp/filelist" 161else 162 right_dir="working_tree" 163 git diff--name-only --$paths>"$tmp/filelist" 164fi 165fi 166 167# Exit immediately if there are no diffs 168iftest!-s"$tmp/filelist" 169then 170exit0 171fi 172 173iftest -n"$copy_back"&&test"$right_dir"!="working_tree" 174then 175echo"--copy-back is only valid when diff includes the working tree." 176exit1 177fi 178 179# Create the named tmp directories that will hold the files to be compared 180mkdir-p"$tmp/$left_dir""$tmp/$right_dir" 181 182# Populate the tmp/right_dir directory with the files to be compared 183iftest -n"$right" 184then 185whileread name 186do 187 ls_list=$(git ls-tree $right "$name") 188iftest -n"$ls_list" 189then 190mkdir-p"$tmp/$right_dir/$(dirname "$name")" 191 git show "$right":"$name">"$tmp/$right_dir/$name"|| true 192fi 193done<"$tmp/filelist" 194eliftest -n"$compare_staged" 195then 196whileread name 197do 198 ls_list=$(git ls-files -- "$name") 199iftest -n"$ls_list" 200then 201mkdir-p"$tmp/$right_dir/$(dirname "$name")" 202 git show :"$name">"$tmp/$right_dir/$name" 203fi 204done<"$tmp/filelist" 205else 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) 209} 210fi 211 212# Populate the tmp/left_dir directory with the files to be compared 213whileread name 214do 215iftest -n"$left" 216then 217 ls_list=$(git ls-tree $left "$name") 218iftest -n"$ls_list" 219then 220mkdir-p"$tmp/$left_dir/$(dirname "$name")" 221 git show "$left":"$name">"$tmp/$left_dir/$name"|| true 222fi 223else 224iftest -n"$compare_staged" 225then 226 ls_list=$(git ls-tree HEAD "$name") 227iftest -n"$ls_list" 228then 229mkdir-p"$tmp/$left_dir/$(dirname "$name")" 230 git show HEAD:"$name">"$tmp/$left_dir/$name" 231fi 232else 233mkdir-p"$tmp/$left_dir/$(dirname "$name")" 234 git show :"$name">"$tmp/$left_dir/$name" 235fi 236fi 237done<"$tmp/filelist" 238 239cd"$tmp" 240LOCAL="$left_dir" 241REMOTE="$right_dir" 242 243iftest -n"$diff_tool" 244then 245export BASE 246eval$diff_tool'"$LOCAL"''"$REMOTE"' 247else 248 run_merge_tool "$merge_tool" false 249fi 250 251# Copy files back to the working dir, if requested 252iftest -n"$copy_back"&&test"$right_dir"="working_tree" 253then 254cd"$start_dir" 255 git_top_dir=$(git rev-parse --show-toplevel) 256find"$tmp/$right_dir"-type f | 257whilereadfile 258do 259cp"$file""$git_top_dir/${file#$tmp/$right_dir/}" 260done 261fi