contrib / diffall / git-diffallon commit Merge branch 'vr/branch-doc' (2c54517)
   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 $REMOTE when this option is
  20                 specified. Additionally, $BASE is 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)"
  31if test -z "$merge_tool"
  32then
  33        echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set."
  34        usage
  35fi
  36
  37start_dir=$(pwd)
  38
  39# needed to access tar utility
  40cdup=$(git rev-parse --show-cdup) &&
  41cd "$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
  48tmp="$(mktemp -d -t tmp.XXXXXX 2>/dev/null)" || {
  49        tmp=/tmp/git-diffall-tmp.$$
  50        mkdir "$tmp" || exit 1
  51}
  52
  53trap 'rm -rf "$tmp" 2>/dev/null' EXIT
  54
  55left=
  56right=
  57paths=
  58dashdash_seen=
  59compare_staged=
  60merge_base=
  61left_dir=
  62right_dir=
  63diff_tool=
  64copy_back=
  65
  66while test $# != 0
  67do
  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
 125done
 126
 127# Determine the set of files which changed
 128if test -n "$left" && test -n "$right"
 129then
 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
 142elif test -n "$left"
 143then
 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
 154else
 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
 165fi
 166
 167# Exit immediately if there are no diffs
 168if test ! -s "$tmp/filelist"
 169then
 170        exit 0
 171fi
 172
 173if test -n "$copy_back" && test "$right_dir" != "working_tree"
 174then
 175        echo "--copy-back is only valid when diff includes the working tree."
 176        exit 1
 177fi
 178
 179# Create the named tmp directories that will hold the files to be compared
 180mkdir -p "$tmp/$left_dir" "$tmp/$right_dir"
 181
 182# Populate the tmp/right_dir directory with the files to be compared
 183if test -n "$right"
 184then
 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"
 194elif test -n "$compare_staged"
 195then
 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"
 205else
 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        }
 210fi
 211
 212# Populate the tmp/left_dir directory with the files to be compared
 213while read name
 214do
 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
 237done < "$tmp/filelist"
 238
 239cd "$tmp"
 240LOCAL="$left_dir"
 241REMOTE="$right_dir"
 242
 243if test -n "$diff_tool"
 244then
 245        export BASE
 246        eval $diff_tool '"$LOCAL"' '"$REMOTE"'
 247else
 248        run_merge_tool "$merge_tool" false
 249fi
 250
 251# Copy files back to the working dir, if requested
 252if test -n "$copy_back" && test "$right_dir" = "working_tree"
 253then
 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
 261fi