1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
#!/bin/sh
#
# Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2005 Junio C Hamano
#
case "$0" in
*-revert* )
test -t 0 && edit=-e
replay=
me=revert
USAGE='[--edit | --no-edit] [-n] <commit-ish>' ;;
*-cherry-pick* )
replay=t
edit=
me=cherry-pick
USAGE='[--edit] [-n] [-r] [-x] <commit-ish>' ;;
* )
echo >&2 "What are you talking about?"
exit 1 ;;
esac
SUBDIRECTORY_OK=Yes ;# we will cd up
. git-sh-setup
require_work_tree
cd_to_toplevel
no_commit=
xopt=
while case "$#" in 0) break ;; esac
do
case "$1" in
-n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\
--no-commi|--no-commit)
no_commit=t
;;
-e|--e|--ed|--edi|--edit)
edit=-e
;;
--n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit)
edit=
;;
-r)
: no-op ;;
-x|--i-really-want-to-expose-my-private-commit-object-name)
replay=
;;
-X?*)
xopt="$xopt$(git rev-parse --sq-quote "--${1#-X}")"
;;
--strategy-option=*)
xopt="$xopt$(git rev-parse --sq-quote "--${1#--strategy-option=}")"
;;
-X|--strategy-option)
shift
xopt="$xopt$(git rev-parse --sq-quote "--$1")"
;;
-*)
usage
;;
*)
break
;;
esac
shift
done
set_reflog_action "$me"
test "$me,$replay" = "revert,t" && usage
case "$no_commit" in
t)
# We do not intend to commit immediately. We just want to
# merge the differences in.
head=$(git-write-tree) ||
die "Your index file is unmerged."
;;
*)
head=$(git-rev-parse --verify HEAD) ||
die "You do not have a valid HEAD"
files=$(git-diff-index --cached --name-only $head) || exit
if [ "$files" ]; then
die "Dirty index: cannot $me (dirty: $files)"
fi
;;
esac
rev=$(git-rev-parse --verify "$@") &&
commit=$(git-rev-parse --verify "$rev^0") ||
die "Not a single commit $@"
prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) ||
die "Cannot run $me a root commit"
git-rev-parse --verify "$commit^2" >/dev/null 2>&1 &&
die "Cannot run $me a multi-parent commit."
encoding=$(git config i18n.commitencoding || echo UTF-8)
# "commit" is an existing commit. We would want to apply
# the difference it introduces since its first parent "prev"
# on top of the current HEAD if we are cherry-pick. Or the
# reverse of it if we are revert.
case "$me" in
revert)
git show -s --pretty=oneline --encoding="$encoding" $commit |
sed -e '
s/^[^ ]* /Revert "/
s/$/"/
'
echo
echo "This reverts commit $commit."
test "$rev" = "$commit" ||
echo "(original 'git revert' arguments: $@)"
base=$commit next=$prev
;;
cherry-pick)
pick_author_script='
/^author /{
s/'\''/'\''\\'\'\''/g
h
s/^author \([^<]*\) <[^>]*> .*$/\1/
s/'\''/'\''\'\'\''/g
s/.*/GIT_AUTHOR_NAME='\''&'\''/p
g
s/^author [^<]* <\([^>]*\)> .*$/\1/
s/'\''/'\''\'\'\''/g
s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
g
s/^author [^<]* <[^>]*> \(.*\)$/\1/
s/'\''/'\''\'\'\''/g
s/.*/GIT_AUTHOR_DATE='\''&'\''/p
q
}'
logmsg=$(git show -s --pretty=raw --encoding="$encoding" "$commit")
set_author_env=$(echo "$logmsg" |
LANG=C LC_ALL=C sed -ne "$pick_author_script")
eval "$set_author_env"
export GIT_AUTHOR_NAME
export GIT_AUTHOR_EMAIL
export GIT_AUTHOR_DATE
echo "$logmsg" |
sed -e '1,/^$/d' -e 's/^ //'
case "$replay" in
'')
echo "(cherry picked from commit $commit)"
test "$rev" = "$commit" ||
echo "(original 'git cherry-pick' arguments: $@)"
;;
esac
base=$prev next=$commit
;;
esac >.msg
eval GITHEAD_$head=HEAD
eval GITHEAD_$next='$(git show -s \
--pretty=oneline --encoding="$encoding" "$commit" |
sed -e "s/^[^ ]* //")'
export GITHEAD_$head GITHEAD_$next
# This three way merge is an interesting one. We are at
# $head, and would want to apply the change between $commit
# and $prev on top of us (when reverting), or the change between
# $prev and $commit on top of us (when cherry-picking or replaying).
eval "git merge-recursive $xopt $base -- $head $next" &&
result=$(git-write-tree 2>/dev/null) || {
mv -f .msg "$GIT_DIR/MERGE_MSG"
{
echo '
Conflicts:
'
git ls-files --unmerged |
sed -e 's/^[^ ]* / /' |
uniq
} >>"$GIT_DIR/MERGE_MSG"
echo >&2 "Automatic $me failed. After resolving the conflicts,"
echo >&2 "mark the corrected paths with 'git-add <paths>'"
echo >&2 "and commit the result."
case "$me" in
cherry-pick)
echo >&2 "You may choose to use the following when making"
echo >&2 "the commit:"
echo >&2 "$set_author_env"
esac
exit 1
}
# If we are cherry-pick, and if the merge did not result in
# hand-editing, we will hit this commit and inherit the original
# author date and name.
# If we are revert, or if our cherry-pick results in a hand merge,
# we had better say that the current user is responsible for that.
case "$no_commit" in
'')
git-commit -n -F .msg $edit
rm -f .msg
;;
esac
|