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