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