05bd854f4b316a34020cd3a600be0518313f9319
   1#!/bin/sh
   2#
   3# Copyright (c) 2005 Linus Torvalds
   4# Copyright (c) 2005 Junio C Hamano
   5#
   6. git-sh-setup || die "Not a git archive"
   7
   8case "$0" in
   9*-revert* )
  10        me=revert ;;
  11*-cherry-pick* )
  12        me=cherry-pick ;;
  13* )
  14        die "What are ou talking about?" ;;
  15esac
  16
  17usage () {
  18        case "$me" in
  19        cherry-pick)
  20                die "usage git $me [-n] [-r] <commit-ish>"
  21                ;;
  22        revert)
  23                die "usage git $me [-n] <commit-ish>"
  24                ;;
  25        esac
  26}
  27
  28no_commit= replay= edit=
  29while case "$#" in 0) break ;; esac
  30do
  31        case "$1" in
  32        -n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\
  33            --no-commi|--no-commit)
  34                no_commit=t
  35                ;;
  36        -r|--r|--re|--rep|--repl|--repla|--replay)
  37                replay=t
  38                ;;
  39        -e|--edit)
  40                edit=t
  41                ;;
  42        -*)
  43                usage
  44                ;;
  45        *)
  46                break
  47                ;;
  48        esac
  49        shift
  50done
  51
  52test "$me,$replay" = "revert,t" && usage
  53
  54case "$no_commit" in
  55t)
  56        # We do not intend to commit immediately.  We just want to
  57        # merge the differences in.
  58        head=$(git-write-tree) ||
  59                die "Your index file is unmerged."
  60        ;;
  61*)
  62        head=$(git-rev-parse --verify HEAD) ||
  63                die "You do not have a valid HEAD"
  64        files=$(git-diff-index --cached --name-only $head) || exit
  65        if [ "$files" ]; then
  66                die "Dirty index: cannot $me (dirty: $files)"
  67        fi
  68        ;;
  69esac
  70
  71rev=$(git-rev-parse --verify "$@") &&
  72commit=$(git-rev-parse --verify "$rev^0") ||
  73        die "Not a single commit $@"
  74prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) ||
  75        die "Cannot run $me a root commit"
  76git-rev-parse --verify "$commit^2" >/dev/null 2>&1 &&
  77        die "Cannot run $me a multi-parent commit."
  78
  79# "commit" is an existing commit.  We would want to apply
  80# the difference it introduces since its first parent "prev"
  81# on top of the current HEAD if we are cherry-pick.  Or the
  82# reverse of it if we are revert.
  83
  84case "$me" in
  85revert)
  86        git-rev-list --pretty=oneline --max-count=1 $commit |
  87        sed -e '
  88                s/^[^ ]* /Revert "/
  89                s/$/"/'
  90        echo
  91        echo "This reverts $commit commit."
  92        test "$rev" = "$commit" ||
  93        echo "(original 'git revert' arguments: $@)"
  94        base=$commit next=$prev
  95        ;;
  96
  97cherry-pick)
  98        pick_author_script='
  99        /^author /{
 100                h
 101                s/^author \([^<]*\) <[^>]*> .*$/\1/
 102                s/'\''/'\''\'\'\''/g
 103                s/.*/GIT_AUTHOR_NAME='\''&'\''/p
 104
 105                g
 106                s/^author [^<]* <\([^>]*\)> .*$/\1/
 107                s/'\''/'\''\'\'\''/g
 108                s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
 109
 110                g
 111                s/^author [^<]* <[^>]*> \(.*\)$/\1/
 112                s/'\''/'\''\'\'\''/g
 113                s/.*/GIT_AUTHOR_DATE='\''&'\''/p
 114
 115                q
 116        }'
 117        set_author_env=`git-cat-file commit "$commit" |
 118        LANG=C LC_ALL=C sed -ne "$pick_author_script"`
 119        eval "$set_author_env"
 120        export GIT_AUTHOR_NAME
 121        export GIT_AUTHOR_EMAIL
 122        export GIT_AUTHOR_DATE
 123
 124        git-cat-file commit $commit | sed -e '1,/^$/d'
 125        case "$replay" in
 126        '')
 127                echo "(cherry picked from $commit commit)"
 128                test "$rev" = "$commit" ||
 129                echo "(original 'git cherry-pick' arguments: $@)"
 130                ;;
 131        esac
 132        base=$prev next=$commit
 133        ;;
 134
 135esac >.msg
 136
 137# This three way merge is an interesting one.  We are at
 138# $head, and would want to apply the change between $commit
 139# and $prev on top of us (when reverting), or the change between
 140# $prev and $commit on top of us (when cherry-picking or replaying).
 141
 142echo >&2 "First trying simple merge strategy to $me."
 143git-read-tree -m -u $base $head $next &&
 144result=$(git-write-tree 2>/dev/null) || {
 145    echo >&2 "Simple $me fails; trying Automatic $me."
 146    git-merge-index -o git-merge-one-file -a || {
 147            echo >&2 "Automatic $me failed.  After fixing it up,"
 148            echo >&2 "you can use \"git commit -F .msg\""
 149            case "$me" in
 150            cherry-pick)
 151                echo >&2 "You may choose to use the following when making"
 152                echo >&2 "the commit:"
 153                echo >&2 "$set_author_env"
 154            esac
 155            exit 1
 156    }
 157    result=$(git-write-tree) || exit
 158}
 159echo >&2 "Finished one $me."
 160
 161# If we are cherry-pick, and if the merge did not result in
 162# hand-editing, we will hit this commit and inherit the original
 163# author date and name.
 164# If we are revert, or if our cherry-pick results in a hand merge,
 165# we had better say that the current user is responsible for that.
 166
 167[ "$edit" ] && ${EDITOR:-${VISUAL:-vi}} .msg
 168case "$no_commit" in
 169'')
 170        git-commit -n -F .msg
 171        rm -f .msg
 172        ;;
 173esac