git-applypatch.shon commit daemon: do not die on older clients. (83543a2)
   1#!/bin/sh
   2##
   3## applypatch takes four file arguments, and uses those to
   4## apply the unpacked patch (surprise surprise) that they
   5## represent to the current tree.
   6##
   7## The arguments are:
   8##      $1 - file with commit message
   9##      $2 - file with the actual patch
  10##      $3 - "info" file with Author, email and subject
  11##      $4 - optional file containing signoff to add
  12##
  13
  14USAGE='<msg> <patch> <info> [<signoff>]'
  15. git-sh-setup
  16
  17case "$#" in 3|4) ;; *) usage ;; esac
  18
  19final=.dotest/final-commit
  20##
  21## If this file exists, we ask before applying
  22##
  23query_apply=.dotest/.query_apply
  24
  25## We do not munge the first line of the commit message too much
  26## if this file exists.
  27keep_subject=.dotest/.keep_subject
  28
  29## We do not attempt the 3-way merge fallback unless this file exists.
  30fall_back_3way=.dotest/.3way
  31
  32MSGFILE=$1
  33PATCHFILE=$2
  34INFO=$3
  35SIGNOFF=$4
  36EDIT=${VISUAL:-${EDITOR:-vi}}
  37
  38export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$INFO")"
  39export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$INFO")"
  40export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$INFO")"
  41export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$INFO")"
  42
  43if test '' != "$SIGNOFF"
  44then
  45        if test -f "$SIGNOFF"
  46        then
  47                SIGNOFF=`cat "$SIGNOFF"` || exit
  48        elif case "$SIGNOFF" in yes | true | me | please) : ;; *) false ;; esac
  49        then
  50                SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
  51                                s/>.*/>/
  52                                s/^/Signed-off-by: /'
  53                `
  54        else
  55                SIGNOFF=
  56        fi
  57        if test '' != "$SIGNOFF"
  58        then
  59                LAST_SIGNED_OFF_BY=`
  60                        sed -ne '/^Signed-off-by: /p' "$MSGFILE" |
  61                        tail -n 1
  62                `
  63                test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
  64                    test '' = "$LAST_SIGNED_OFF_BY" && echo
  65                    echo "$SIGNOFF"
  66                } >>"$MSGFILE"
  67        fi
  68fi
  69
  70patch_header=
  71test -f "$keep_subject" || patch_header='[PATCH] '
  72
  73{
  74        echo "$patch_header$SUBJECT"
  75        if test -s "$MSGFILE"
  76        then
  77                echo
  78                cat "$MSGFILE"
  79        fi
  80} >"$final"
  81
  82interactive=yes
  83test -f "$query_apply" || interactive=no
  84
  85while [ "$interactive" = yes ]; do
  86        echo "Commit Body is:"
  87        echo "--------------------------"
  88        cat "$final"
  89        echo "--------------------------"
  90        printf "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
  91        read reply
  92        case "$reply" in
  93                y|Y) interactive=no;;
  94                n|N) exit 2;;   # special value to tell dotest to keep going
  95                e|E) "$EDIT" "$final";;
  96                a|A) rm -f "$query_apply"
  97                     interactive=no ;;
  98        esac
  99done
 100
 101if test -x "$GIT_DIR"/hooks/applypatch-msg
 102then
 103        "$GIT_DIR"/hooks/applypatch-msg "$final" || exit
 104fi
 105
 106echo
 107echo Applying "'$SUBJECT'"
 108echo
 109
 110git-apply --index "$PATCHFILE" || {
 111
 112        # git-apply exits with status 1 when the patch does not apply,
 113        # but it die()s with other failures, most notably upon corrupt
 114        # patch.  In the latter case, there is no point to try applying
 115        # it to another tree and do 3-way merge.
 116        test $? = 1 || exit 1
 117
 118        test -f "$fall_back_3way" || exit 1
 119
 120        # Here if we know which revision the patch applies to,
 121        # we create a temporary working tree and index, apply the
 122        # patch, and attempt 3-way merge with the resulting tree.
 123
 124        O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
 125        rm -fr .patch-merge-*
 126
 127        if git-apply -z --index-info "$PATCHFILE" \
 128                >.patch-merge-index-info 2>/dev/null &&
 129                GIT_INDEX_FILE=.patch-merge-tmp-index \
 130                git-update-index -z --index-info <.patch-merge-index-info &&
 131                GIT_INDEX_FILE=.patch-merge-tmp-index \
 132                git-write-tree >.patch-merge-tmp-base &&
 133                (
 134                        mkdir .patch-merge-tmp-dir &&
 135                        cd .patch-merge-tmp-dir &&
 136                        GIT_INDEX_FILE="../.patch-merge-tmp-index" \
 137                        GIT_OBJECT_DIRECTORY="$O_OBJECT" \
 138                        git-apply $binary --index
 139                ) <"$PATCHFILE"
 140        then
 141                echo Using index info to reconstruct a base tree...
 142                mv .patch-merge-tmp-base .patch-merge-base
 143                mv .patch-merge-tmp-index .patch-merge-index
 144        else
 145        (
 146                N=10
 147
 148                # Otherwise, try nearby trees that can be used to apply the
 149                # patch.
 150                git-rev-list --max-count=$N HEAD
 151
 152                # or hoping the patch is against known tags...
 153                git-ls-remote --tags .
 154        ) |
 155            while read base junk
 156            do
 157                # Try it if we have it as a tree.
 158                git-cat-file tree "$base" >/dev/null 2>&1 || continue
 159
 160                rm -fr .patch-merge-tmp-* &&
 161                mkdir .patch-merge-tmp-dir || break
 162                (
 163                        cd .patch-merge-tmp-dir &&
 164                        GIT_INDEX_FILE=../.patch-merge-tmp-index &&
 165                        GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
 166                        export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
 167                        git-read-tree "$base" &&
 168                        git-apply --index &&
 169                        mv ../.patch-merge-tmp-index ../.patch-merge-index &&
 170                        echo "$base" >../.patch-merge-base
 171                ) <"$PATCHFILE"  2>/dev/null && break
 172            done
 173        fi
 174
 175        test -f .patch-merge-index &&
 176        his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
 177        orig_tree=$(cat .patch-merge-base) &&
 178        rm -fr .patch-merge-* || exit 1
 179
 180        echo Falling back to patching base and 3-way merge using $orig_tree...
 181
 182        # This is not so wrong.  Depending on which base we picked,
 183        # orig_tree may be wildly different from ours, but his_tree
 184        # has the same set of wildly different changes in parts the
 185        # patch did not touch, so resolve ends up canceling them,
 186        # saying that we reverted all those changes.
 187
 188        if git-merge-resolve $orig_tree -- HEAD $his_tree
 189        then
 190                echo Done.
 191        else
 192                echo Failed to merge in the changes.
 193                exit 1
 194        fi
 195}
 196
 197if test -x "$GIT_DIR"/hooks/pre-applypatch
 198then
 199        "$GIT_DIR"/hooks/pre-applypatch || exit
 200fi
 201
 202tree=$(git-write-tree) || exit 1
 203echo Wrote tree $tree
 204parent=$(git-rev-parse --verify HEAD) &&
 205commit=$(git-commit-tree $tree -p $parent <"$final") || exit 1
 206echo Committed: $commit
 207git-update-ref -m "applypatch: $SUBJECT" HEAD $commit $parent || exit
 208
 209if test -x "$GIT_DIR"/hooks/post-applypatch
 210then
 211        "$GIT_DIR"/hooks/post-applypatch
 212fi