git-revert.shon commit clone: the given repository dir should be relative to $PWD (ced78b3)
   1#!/bin/sh
   2#
   3# Copyright (c) 2005 Linus Torvalds
   4# Copyright (c) 2005 Junio C Hamano
   5#
   6
   7case "$0" in
   8*-revert* )
   9        test -t 0 && edit=-e
  10        replay=
  11        me=revert
  12        USAGE='[--edit | --no-edit] [-n] <commit-ish>' ;;
  13*-cherry-pick* )
  14        replay=t
  15        edit=
  16        me=cherry-pick
  17        USAGE='[--edit] [-n] [-r] [-x] <commit-ish>'  ;;
  18* )
  19        die "What are you talking about?" ;;
  20esac
  21. git-sh-setup
  22
  23no_commit=
  24while case "$#" in 0) break ;; esac
  25do
  26        case "$1" in
  27        -n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\
  28            --no-commi|--no-commit)
  29                no_commit=t
  30                ;;
  31        -e|--e|--ed|--edi|--edit)
  32                edit=-e
  33                ;;
  34        --n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit)
  35                edit=
  36                ;;
  37        -r)
  38                : no-op ;;
  39        -x|--i-really-want-to-expose-my-private-commit-object-name)
  40                replay=
  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                s/'\''/'\''\\'\'\''/g
 101                h
 102                s/^author \([^<]*\) <[^>]*> .*$/\1/
 103                s/'\''/'\''\'\'\''/g
 104                s/.*/GIT_AUTHOR_NAME='\''&'\''/p
 105
 106                g
 107                s/^author [^<]* <\([^>]*\)> .*$/\1/
 108                s/'\''/'\''\'\'\''/g
 109                s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
 110
 111                g
 112                s/^author [^<]* <[^>]*> \(.*\)$/\1/
 113                s/'\''/'\''\'\'\''/g
 114                s/.*/GIT_AUTHOR_DATE='\''&'\''/p
 115
 116                q
 117        }'
 118        set_author_env=`git-cat-file commit "$commit" |
 119        LANG=C LC_ALL=C sed -ne "$pick_author_script"`
 120        eval "$set_author_env"
 121        export GIT_AUTHOR_NAME
 122        export GIT_AUTHOR_EMAIL
 123        export GIT_AUTHOR_DATE
 124
 125        git-cat-file commit $commit | sed -e '1,/^$/d'
 126        case "$replay" in
 127        '')
 128                echo "(cherry picked from commit $commit)"
 129                test "$rev" = "$commit" ||
 130                echo "(original 'git cherry-pick' arguments: $@)"
 131                ;;
 132        esac
 133        base=$prev next=$commit
 134        ;;
 135
 136esac >.msg
 137
 138# This three way merge is an interesting one.  We are at
 139# $head, and would want to apply the change between $commit
 140# and $prev on top of us (when reverting), or the change between
 141# $prev and $commit on top of us (when cherry-picking or replaying).
 142
 143echo >&2 "First trying simple merge strategy to $me."
 144git-read-tree -m -u --aggressive $base $head $next &&
 145result=$(git-write-tree 2>/dev/null) || {
 146    echo >&2 "Simple $me fails; trying Automatic $me."
 147    git-merge-index -o git-merge-one-file -a || {
 148            echo >&2 "Automatic $me failed.  After resolving the conflicts,"
 149            echo >&2 "mark the corrected paths with 'git-update-index <paths>'"
 150            echo >&2 "and commit with 'git commit -F .msg'"
 151            case "$me" in
 152            cherry-pick)
 153                echo >&2 "You may choose to use the following when making"
 154                echo >&2 "the commit:"
 155                echo >&2 "$set_author_env"
 156            esac
 157            exit 1
 158    }
 159    result=$(git-write-tree) || exit
 160}
 161echo >&2 "Finished one $me."
 162
 163# If we are cherry-pick, and if the merge did not result in
 164# hand-editing, we will hit this commit and inherit the original
 165# author date and name.
 166# If we are revert, or if our cherry-pick results in a hand merge,
 167# we had better say that the current user is responsible for that.
 168
 169case "$no_commit" in
 170'')
 171        git-commit -n -F .msg $edit
 172        rm -f .msg
 173        ;;
 174esac