3a129e0021f4847ac6f717a97b69b5a399088c99
   1#!/bin/sh
   2#
   3# Copyright (c) 2005, 2006 Junio C Hamano
   4
   5USAGE='[--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way]
   6  [--interactive] [--whitespace=<option>] <mbox>...
   7  or, when resuming [--skip | --resolved]'
   8. git-sh-setup
   9
  10git var GIT_COMMITTER_IDENT >/dev/null || exit
  11
  12stop_here () {
  13    echo "$1" >"$dotest/next"
  14    exit 1
  15}
  16
  17stop_here_user_resolve () {
  18    if [ -n "$resolvemsg" ]; then
  19            echo "$resolvemsg"
  20            stop_here $1
  21    fi
  22    cmdline=$(basename $0)
  23    if test '' != "$interactive"
  24    then
  25        cmdline="$cmdline -i"
  26    fi
  27    if test '' != "$threeway"
  28    then
  29        cmdline="$cmdline -3"
  30    fi
  31    if test '.dotest' != "$dotest"
  32    then
  33        cmdline="$cmdline -d=$dotest"
  34    fi
  35    echo "When you have resolved this problem run \"$cmdline --resolved\"."
  36    echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
  37
  38    stop_here $1
  39}
  40
  41go_next () {
  42        rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
  43                "$dotest/patch" "$dotest/info"
  44        echo "$next" >"$dotest/next"
  45        this=$next
  46}
  47
  48fall_back_3way () {
  49    O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
  50
  51    rm -fr "$dotest"/patch-merge-*
  52    mkdir "$dotest/patch-merge-tmp-dir"
  53
  54    # First see if the patch records the index info that we can use.
  55    if git-apply -z --index-info "$dotest/patch" \
  56        >"$dotest/patch-merge-index-info" 2>/dev/null &&
  57        GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
  58        git-update-index -z --index-info <"$dotest/patch-merge-index-info" &&
  59        GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
  60        git-write-tree >"$dotest/patch-merge-base+" &&
  61        # index has the base tree now.
  62        GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
  63        git-apply $binary --cached <"$dotest/patch"
  64    then
  65        echo Using index info to reconstruct a base tree...
  66        mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
  67        mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
  68    fi
  69
  70    test -f "$dotest/patch-merge-index" &&
  71    his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git-write-tree) &&
  72    orig_tree=$(cat "$dotest/patch-merge-base") &&
  73    rm -fr "$dotest"/patch-merge-* || exit 1
  74
  75    echo Falling back to patching base and 3-way merge...
  76
  77    # This is not so wrong.  Depending on which base we picked,
  78    # orig_tree may be wildly different from ours, but his_tree
  79    # has the same set of wildly different changes in parts the
  80    # patch did not touch, so resolve ends up canceling them,
  81    # saying that we reverted all those changes.
  82
  83    git-merge-resolve $orig_tree -- HEAD $his_tree || {
  84            if test -d "$GIT_DIR/rr-cache"
  85            then
  86                git-rerere
  87            fi
  88            echo Failed to merge in the changes.
  89            exit 1
  90    }
  91}
  92
  93prec=4
  94rloga=am
  95dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= resolvemsg=
  96
  97while case "$#" in 0) break;; esac
  98do
  99        case "$1" in
 100        -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
 101        dotest=`expr "z$1" : 'z-[^=]*=\(.*\)'`; shift ;;
 102        -d|--d|--do|--dot|--dote|--dotes|--dotest)
 103        case "$#" in 1) usage ;; esac; shift
 104        dotest="$1"; shift;;
 105
 106        -i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\
 107        --interacti|--interactiv|--interactive)
 108        interactive=t; shift ;;
 109
 110        -b|--b|--bi|--bin|--bina|--binar|--binary)
 111        binary=t; shift ;;
 112
 113        -3|--3|--3w|--3wa|--3way)
 114        threeway=t; shift ;;
 115        -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
 116        sign=t; shift ;;
 117        -u|--u|--ut|--utf|--utf8)
 118        utf8=t; shift ;;
 119        -k|--k|--ke|--kee|--keep)
 120        keep=t; shift ;;
 121
 122        -r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved)
 123        resolved=t; shift ;;
 124
 125        --sk|--ski|--skip)
 126        skip=t; shift ;;
 127
 128        --whitespace=*)
 129        ws=$1; shift ;;
 130
 131        --resolvemsg=*)
 132        resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//"); shift ;;
 133
 134        --reflog-action=*)
 135        rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`; shift ;;
 136
 137        --)
 138        shift; break ;;
 139        -*)
 140        usage ;;
 141        *)
 142        break ;;
 143        esac
 144done
 145
 146# If the dotest directory exists, but we have finished applying all the
 147# patches in them, clear it out.
 148if test -d "$dotest" &&
 149   last=$(cat "$dotest/last") &&
 150   next=$(cat "$dotest/next") &&
 151   test $# != 0 &&
 152   test "$next" -gt "$last"
 153then
 154   rm -fr "$dotest"
 155fi
 156
 157if test -d "$dotest"
 158then
 159        test ",$#," = ",0," ||
 160        die "previous dotest directory $dotest still exists but mbox given."
 161        resume=yes
 162else
 163        # Make sure we are not given --skip nor --resolved
 164        test ",$skip,$resolved," = ,,, ||
 165                die "Resolve operation not in progress, we are not resuming."
 166
 167        # Start afresh.
 168        mkdir -p "$dotest" || exit
 169
 170        git-mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
 171                rm -fr "$dotest"
 172                exit 1
 173        }
 174
 175        # -b, -s, -u, -k and --whitespace flags are kept for the
 176        # resuming session after a patch failure.
 177        # -3 and -i can and must be given when resuming.
 178        echo "$binary" >"$dotest/binary"
 179        echo " $ws" >"$dotest/whitespace"
 180        echo "$sign" >"$dotest/sign"
 181        echo "$utf8" >"$dotest/utf8"
 182        echo "$keep" >"$dotest/keep"
 183        echo 1 >"$dotest/next"
 184fi
 185
 186case "$resolved" in
 187'')
 188        files=$(git-diff-index --cached --name-only HEAD) || exit
 189        if [ "$files" ]; then
 190           echo "Dirty index: cannot apply patches (dirty: $files)" >&2
 191           exit 1
 192        fi
 193esac
 194
 195if test "$(cat "$dotest/binary")" = t
 196then
 197        binary=--allow-binary-replacement
 198fi
 199if test "$(cat "$dotest/utf8")" = t
 200then
 201        utf8=-u
 202fi
 203if test "$(cat "$dotest/keep")" = t
 204then
 205        keep=-k
 206fi
 207ws=`cat "$dotest/whitespace"`
 208if test "$(cat "$dotest/sign")" = t
 209then
 210        SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
 211                        s/>.*/>/
 212                        s/^/Signed-off-by: /'
 213                `
 214else
 215        SIGNOFF=
 216fi
 217
 218last=`cat "$dotest/last"`
 219this=`cat "$dotest/next"`
 220if test "$skip" = t
 221then
 222        this=`expr "$this" + 1`
 223        resume=
 224fi
 225
 226if test "$this" -gt "$last"
 227then
 228        echo Nothing to do.
 229        rm -fr "$dotest"
 230        exit
 231fi
 232
 233while test "$this" -le "$last"
 234do
 235        msgnum=`printf "%0${prec}d" $this`
 236        next=`expr "$this" + 1`
 237        test -f "$dotest/$msgnum" || {
 238                resume=
 239                go_next
 240                continue
 241        }
 242
 243        # If we are not resuming, parse and extract the patch information
 244        # into separate files:
 245        #  - info records the authorship and title
 246        #  - msg is the rest of commit log message
 247        #  - patch is the patch body.
 248        #
 249        # When we are resuming, these files are either already prepared
 250        # by the user, or the user can tell us to do so by --resolved flag.
 251        case "$resume" in
 252        '')
 253                git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
 254                        <"$dotest/$msgnum" >"$dotest/info" ||
 255                        stop_here $this
 256                git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
 257                ;;
 258        esac
 259
 260        GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
 261        GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
 262        GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
 263
 264        if test -z "$GIT_AUTHOR_EMAIL"
 265        then
 266                echo "Patch does not have a valid e-mail address."
 267                stop_here $this
 268        fi
 269
 270        export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
 271
 272        SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
 273        case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
 274
 275        case "$resume" in
 276        '')
 277            if test '' != "$SIGNOFF"
 278            then
 279                LAST_SIGNED_OFF_BY=`
 280                    sed -ne '/^Signed-off-by: /p' \
 281                    "$dotest/msg-clean" |
 282                    tail -n 1
 283                `
 284                ADD_SIGNOFF=`
 285                    test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
 286                    test '' = "$LAST_SIGNED_OFF_BY" && echo
 287                    echo "$SIGNOFF"
 288                }`
 289            else
 290                ADD_SIGNOFF=
 291            fi
 292            {
 293                echo "$SUBJECT"
 294                if test -s "$dotest/msg-clean"
 295                then
 296                        echo
 297                        cat "$dotest/msg-clean"
 298                fi
 299                if test '' != "$ADD_SIGNOFF"
 300                then
 301                        echo "$ADD_SIGNOFF"
 302                fi
 303            } >"$dotest/final-commit"
 304            ;;
 305        *)
 306                case "$resolved$interactive" in
 307                tt)
 308                        # This is used only for interactive view option.
 309                        git-diff-index -p --cached HEAD >"$dotest/patch"
 310                        ;;
 311                esac
 312        esac
 313
 314        resume=
 315        if test "$interactive" = t
 316        then
 317            test -t 0 ||
 318            die "cannot be interactive without stdin connected to a terminal."
 319            action=again
 320            while test "$action" = again
 321            do
 322                echo "Commit Body is:"
 323                echo "--------------------------"
 324                cat "$dotest/final-commit"
 325                echo "--------------------------"
 326                printf "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 327                read reply
 328                case "$reply" in
 329                [yY]*) action=yes ;;
 330                [aA]*) action=yes interactive= ;;
 331                [nN]*) action=skip ;;
 332                [eE]*) "${VISUAL:-${EDITOR:-vi}}" "$dotest/final-commit"
 333                       action=again ;;
 334                [vV]*) action=again
 335                       LESS=-S ${PAGER:-less} "$dotest/patch" ;;
 336                *)     action=again ;;
 337                esac
 338            done
 339        else
 340            action=yes
 341        fi
 342
 343        if test $action = skip
 344        then
 345                go_next
 346                continue
 347        fi
 348
 349        if test -x "$GIT_DIR"/hooks/applypatch-msg
 350        then
 351                "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
 352                stop_here $this
 353        fi
 354
 355        echo
 356        echo "Applying '$SUBJECT'"
 357        echo
 358
 359        case "$resolved" in
 360        '')
 361                git-apply $binary --index $ws "$dotest/patch"
 362                apply_status=$?
 363                ;;
 364        t)
 365                # Resolved means the user did all the hard work, and
 366                # we do not have to do any patch application.  Just
 367                # trust what the user has in the index file and the
 368                # working tree.
 369                resolved=
 370                changed="$(git-diff-index --cached --name-only HEAD)"
 371                if test '' = "$changed"
 372                then
 373                        echo "No changes - did you forget update-index?"
 374                        stop_here_user_resolve $this
 375                fi
 376                unmerged=$(git-ls-files -u)
 377                if test -n "$unmerged"
 378                then
 379                        echo "You still have unmerged paths in your index"
 380                        echo "did you forget update-index?"
 381                        stop_here_user_resolve $this
 382                fi
 383                apply_status=0
 384                ;;
 385        esac
 386
 387        if test $apply_status = 1 && test "$threeway" = t
 388        then
 389                if (fall_back_3way)
 390                then
 391                    # Applying the patch to an earlier tree and merging the
 392                    # result may have produced the same tree as ours.
 393                    changed="$(git-diff-index --cached --name-only HEAD)"
 394                    if test '' = "$changed"
 395                    then
 396                            echo No changes -- Patch already applied.
 397                            go_next
 398                            continue
 399                    fi
 400                    # clear apply_status -- we have successfully merged.
 401                    apply_status=0
 402                fi
 403        fi
 404        if test $apply_status != 0
 405        then
 406                echo Patch failed at $msgnum.
 407                stop_here_user_resolve $this
 408        fi
 409
 410        if test -x "$GIT_DIR"/hooks/pre-applypatch
 411        then
 412                "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
 413        fi
 414
 415        tree=$(git-write-tree) &&
 416        echo Wrote tree $tree &&
 417        parent=$(git-rev-parse --verify HEAD) &&
 418        commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
 419        echo Committed: $commit &&
 420        git-update-ref -m "$rloga: $SUBJECT" HEAD $commit $parent ||
 421        stop_here $this
 422
 423        if test -x "$GIT_DIR"/hooks/post-applypatch
 424        then
 425                "$GIT_DIR"/hooks/post-applypatch
 426        fi
 427
 428        go_next
 429done
 430
 431rm -fr "$dotest"