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