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 182while read name 183do 184 if test -n "$right" 185 then 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 elif test -n "$compare_staged" 193 then 194 ls_list=$(git ls-files -- "$name") 195 if test -n "$ls_list" 196 then 197 mkdir -p "$tmp/$right_dir/$(dirname "$name")" 198 git show :"$name" >"$tmp/$right_dir/$name" 199 fi 200 else 201 if test -e "$name" 202 then 203 mkdir -p "$tmp/$right_dir/$(dirname "$name")" 204 cp "$name" "$tmp/$right_dir/$name" 205 fi 206 fi 207done < "$tmp/filelist" 208 209# Populate the tmp/left_dir directory with the files to be compared 210while read name 211do 212 if test -n "$left" 213 then 214 ls_list=$(git ls-tree $left "$name") 215 if test -n "$ls_list" 216 then 217 mkdir -p "$tmp/$left_dir/$(dirname "$name")" 218 git show "$left":"$name" >"$tmp/$left_dir/$name" || true 219 fi 220 else 221 if test -n "$compare_staged" 222 then 223 ls_list=$(git ls-tree HEAD "$name") 224 if test -n "$ls_list" 225 then 226 mkdir -p "$tmp/$left_dir/$(dirname "$name")" 227 git show HEAD:"$name" >"$tmp/$left_dir/$name" 228 fi 229 else 230 mkdir -p "$tmp/$left_dir/$(dirname "$name")" 231 git show :"$name" >"$tmp/$left_dir/$name" 232 fi 233 fi 234done < "$tmp/filelist" 235 236cd "$tmp" 237LOCAL="$left_dir" 238REMOTE="$right_dir" 239 240if test -n "$diff_tool" 241then 242 export BASE 243 eval$diff_tool'"$LOCAL"' '"$REMOTE"' 244else 245 run_merge_tool "$merge_tool" false 246fi 247 248# Copy files back to the working dir, if requested 249if test -n "$copy_back" && test "$right_dir" = "working_tree" 250then 251 cd "$start_dir" 252 git_top_dir=$(git rev-parse --show-toplevel) 253 find "$tmp/$right_dir" -type f | 254 while read file 255 do 256 cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}" 257 done 258fi