contrib / diffall / git-diffallon commit fetch-pack: avoid quadratic list insertion in mark_complete (1644524)
   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# 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" || {
  44        echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
  45        exit 1
  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"' 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
 236LOCAL="$tmp/$left_dir"
 237REMOTE="$tmp/$right_dir"
 238
 239if test -n "$diff_tool"
 240then
 241        export BASE
 242        eval $diff_tool '"$LOCAL"' '"$REMOTE"'
 243else
 244        run_merge_tool "$merge_tool" false
 245fi
 246
 247# Copy files back to the working dir, if requested
 248if test -n "$copy_back" && test "$right_dir" = "working_tree"
 249then
 250        cd "$start_dir"
 251        git_top_dir=$(git rev-parse --show-toplevel)
 252        find "$tmp/$right_dir" -type f |
 253        while read file
 254        do
 255                cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}"
 256        done
 257fi