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