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