|
| 1 | +#!/bin/sh |
| 2 | +# Copyright 2010 - 2012, Tim Henigan <[email protected]> |
| 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 | + |
| 7 | +USAGE='[--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 $REMOTE when this option is |
| 20 | + specified. Additionally, $BASE is set in the |
| 21 | + environment. |
| 22 | +' |
| 23 | + |
| 24 | +SUBDIRECTORY_OK=1 |
| 25 | +. "$(git --exec-path)/git-sh-setup" |
| 26 | + |
| 27 | +TOOL_MODE=diff |
| 28 | +. "$(git --exec-path)/git-mergetool--lib" |
| 29 | + |
| 30 | +merge_tool="$(get_merge_tool)" |
| 31 | +if test -z "$merge_tool" |
| 32 | +then |
| 33 | + echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set." |
| 34 | + usage |
| 35 | +fi |
| 36 | + |
| 37 | +start_dir=$(pwd) |
| 38 | + |
| 39 | +# needed to access tar utility |
| 40 | +cdup=$(git rev-parse --show-cdup) && |
| 41 | +cd "$cdup" || { |
| 42 | + echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree" |
| 43 | + exit 1 |
| 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 |
| 48 | +tmp="$(mktemp -d -t tmp.XXXXXX 2>/dev/null)" || { |
| 49 | + tmp=/tmp/git-diffall-tmp.$$ |
| 50 | + mkdir "$tmp" || exit 1 |
| 51 | +} |
| 52 | + |
| 53 | +trap 'rm -rf "$tmp" 2>/dev/null' EXIT |
| 54 | + |
| 55 | +left= |
| 56 | +right= |
| 57 | +paths= |
| 58 | +dashdash_seen= |
| 59 | +compare_staged= |
| 60 | +merge_base= |
| 61 | +left_dir= |
| 62 | +right_dir= |
| 63 | +diff_tool= |
| 64 | +copy_back= |
| 65 | + |
| 66 | +while test $# != 0 |
| 67 | +do |
| 68 | + case "$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) |
| 79 | + if test $# = 1 |
| 80 | + then |
| 81 | + echo You must specify the tool for use with --extcmd |
| 82 | + usage |
| 83 | + else |
| 84 | + diff_tool=$2 |
| 85 | + shift |
| 86 | + fi |
| 87 | + ;; |
| 88 | + --) |
| 89 | + dashdash_seen=1 |
| 90 | + ;; |
| 91 | + -*) |
| 92 | + echo Invalid option: "$1" |
| 93 | + usage |
| 94 | + ;; |
| 95 | + *) |
| 96 | + # could be commit, commit range or path limiter |
| 97 | + case "$1" in |
| 98 | + *...*) |
| 99 | + left=${1%...*} |
| 100 | + right=${1#*...} |
| 101 | + merge_base=1 |
| 102 | + ;; |
| 103 | + *..*) |
| 104 | + left=${1%..*} |
| 105 | + right=${1#*..} |
| 106 | + ;; |
| 107 | + *) |
| 108 | + if test -n "$dashdash_seen" |
| 109 | + then |
| 110 | + paths="$paths$1 " |
| 111 | + elif test -z "$left" |
| 112 | + then |
| 113 | + left=$1 |
| 114 | + elif test -z "$right" |
| 115 | + then |
| 116 | + right=$1 |
| 117 | + else |
| 118 | + paths="$paths$1 " |
| 119 | + fi |
| 120 | + ;; |
| 121 | + esac |
| 122 | + ;; |
| 123 | + esac |
| 124 | + shift |
| 125 | +done |
| 126 | + |
| 127 | +# Determine the set of files which changed |
| 128 | +if test -n "$left" && test -n "$right" |
| 129 | +then |
| 130 | + left_dir="cmt-$(git rev-parse --short $left)" |
| 131 | + right_dir="cmt-$(git rev-parse --short $right)" |
| 132 | + |
| 133 | + if test -n "$compare_staged" |
| 134 | + then |
| 135 | + usage |
| 136 | + elif test -n "$merge_base" |
| 137 | + then |
| 138 | + git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist" |
| 139 | + else |
| 140 | + git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist" |
| 141 | + fi |
| 142 | +elif test -n "$left" |
| 143 | +then |
| 144 | + left_dir="cmt-$(git rev-parse --short $left)" |
| 145 | + |
| 146 | + if test -n "$compare_staged" |
| 147 | + then |
| 148 | + right_dir="staged" |
| 149 | + git diff --name-only --cached "$left" -- $paths >"$tmp/filelist" |
| 150 | + else |
| 151 | + right_dir="working_tree" |
| 152 | + git diff --name-only "$left" -- $paths >"$tmp/filelist" |
| 153 | + fi |
| 154 | +else |
| 155 | + left_dir="HEAD" |
| 156 | + |
| 157 | + if test -n "$compare_staged" |
| 158 | + then |
| 159 | + right_dir="staged" |
| 160 | + git diff --name-only --cached -- $paths >"$tmp/filelist" |
| 161 | + else |
| 162 | + right_dir="working_tree" |
| 163 | + git diff --name-only -- $paths >"$tmp/filelist" |
| 164 | + fi |
| 165 | +fi |
| 166 | + |
| 167 | +# Exit immediately if there are no diffs |
| 168 | +if test ! -s "$tmp/filelist" |
| 169 | +then |
| 170 | + exit 0 |
| 171 | +fi |
| 172 | + |
| 173 | +if test -n "$copy_back" && test "$right_dir" != "working_tree" |
| 174 | +then |
| 175 | + echo "--copy-back is only valid when diff includes the working tree." |
| 176 | + exit 1 |
| 177 | +fi |
| 178 | + |
| 179 | +# Create the named tmp directories that will hold the files to be compared |
| 180 | +mkdir -p "$tmp/$left_dir" "$tmp/$right_dir" |
| 181 | + |
| 182 | +# Populate the tmp/right_dir directory with the files to be compared |
| 183 | +if test -n "$right" |
| 184 | +then |
| 185 | + while read name |
| 186 | + do |
| 187 | + ls_list=$(git ls-tree $right "$name") |
| 188 | + if test -n "$ls_list" |
| 189 | + then |
| 190 | + mkdir -p "$tmp/$right_dir/$(dirname "$name")" |
| 191 | + git show "$right":"$name" >"$tmp/$right_dir/$name" || true |
| 192 | + fi |
| 193 | + done < "$tmp/filelist" |
| 194 | +elif test -n "$compare_staged" |
| 195 | +then |
| 196 | + while read name |
| 197 | + do |
| 198 | + ls_list=$(git ls-files -- "$name") |
| 199 | + if test -n "$ls_list" |
| 200 | + then |
| 201 | + mkdir -p "$tmp/$right_dir/$(dirname "$name")" |
| 202 | + git show :"$name" >"$tmp/$right_dir/$name" |
| 203 | + fi |
| 204 | + done < "$tmp/filelist" |
| 205 | +else |
| 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 | + } |
| 210 | +fi |
| 211 | + |
| 212 | +# Populate the tmp/left_dir directory with the files to be compared |
| 213 | +while read name |
| 214 | +do |
| 215 | + if test -n "$left" |
| 216 | + then |
| 217 | + ls_list=$(git ls-tree $left "$name") |
| 218 | + if test -n "$ls_list" |
| 219 | + then |
| 220 | + mkdir -p "$tmp/$left_dir/$(dirname "$name")" |
| 221 | + git show "$left":"$name" >"$tmp/$left_dir/$name" || true |
| 222 | + fi |
| 223 | + else |
| 224 | + if test -n "$compare_staged" |
| 225 | + then |
| 226 | + ls_list=$(git ls-tree HEAD "$name") |
| 227 | + if test -n "$ls_list" |
| 228 | + then |
| 229 | + mkdir -p "$tmp/$left_dir/$(dirname "$name")" |
| 230 | + git show HEAD:"$name" >"$tmp/$left_dir/$name" |
| 231 | + fi |
| 232 | + else |
| 233 | + mkdir -p "$tmp/$left_dir/$(dirname "$name")" |
| 234 | + git show :"$name" >"$tmp/$left_dir/$name" |
| 235 | + fi |
| 236 | + fi |
| 237 | +done < "$tmp/filelist" |
| 238 | + |
| 239 | +cd "$tmp" |
| 240 | +LOCAL="$left_dir" |
| 241 | +REMOTE="$right_dir" |
| 242 | + |
| 243 | +if test -n "$diff_tool" |
| 244 | +then |
| 245 | + export BASE |
| 246 | + eval $diff_tool '"$LOCAL"' '"$REMOTE"' |
| 247 | +else |
| 248 | + run_merge_tool "$merge_tool" false |
| 249 | +fi |
| 250 | + |
| 251 | +# Copy files back to the working dir, if requested |
| 252 | +if test -n "$copy_back" && test "$right_dir" = "working_tree" |
| 253 | +then |
| 254 | + cd "$start_dir" |
| 255 | + git_top_dir=$(git rev-parse --show-toplevel) |
| 256 | + find "$tmp/$right_dir" -type f | |
| 257 | + while read file |
| 258 | + do |
| 259 | + cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}" |
| 260 | + done |
| 261 | +fi |
0 commit comments