diff options
Diffstat (limited to 'git-revert-script')
-rwxr-xr-x | git-revert-script | 187 |
1 files changed, 157 insertions, 30 deletions
diff --git a/git-revert-script b/git-revert-script index 22f2082fb1..dd5866ec96 100755 --- a/git-revert-script +++ b/git-revert-script @@ -1,37 +1,164 @@ #!/bin/sh +# +# Copyright (c) 2005 Linus Torvalds +# Copyright (c) 2005 Junio C Hamano +# . git-sh-setup-script || die "Not a git archive" -# We want a clean tree and clean index to be able to revert. -status=$(git status) -case "$status" in -'nothing to commit') ;; +case "$0" in +*-revert-* ) + me=revert ;; +*-cherry-pick-* ) + me=cherry-pick ;; +esac + +usage () { + case "$me" in + cherry-pick) + die "usage git $me [-n] [-r] <commit-ish>" + ;; + revert) + die "usage git $me [-n] <commit-ish>" + ;; + esac +} + +no_commit= replay= +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 + ;; + -r|--r|--re|--rep|--repl|--repla|--replay) + replay=t + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift +done + +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." + ;; *) - echo "$status" - die "Your working tree is dirty; cannot revert a previous patch." ;; + check_clean_tree || die "Cannot run $me from a dirty tree." + head=$(git-rev-parse --verify HEAD) || + die "You do not have a valid HEAD" + ;; esac rev=$(git-rev-parse --verify "$@") && -commit=$(git-rev-parse --verify "$rev^0") || exit -if git-diff-tree -R -M -p $commit | git-apply --index && - msg=$(git-rev-list --pretty=oneline --max-count=1 $commit) -then - { - echo "$msg" | sed -e ' - s/^[^ ]* /Revert "/ - s/$/"/' - echo - echo "This reverts $commit commit." - test "$rev" = "$commit" || - echo "(original 'git revert' arguments: $@)" - } | git commit -F - -else - # Now why did it fail? - parents=`git-cat-file commit "$commit" 2>/dev/null | - sed -ne '/^$/q;/^parent /p' | - wc -l` - case $parents in - 0) die "Cannot revert the root commit nor non commit-ish." ;; - 1) die "The patch does not apply." ;; - *) die "Cannot revert a merge commit." ;; - esac -fi +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." + +# "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-rev-list --pretty=oneline --max-count=1 $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 /{ + 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 + }' + set_author_env=`git-cat-file commit "$commit" | + sed -ne "$pick_author_script"` + eval "$set_author_env" + export GIT_AUTHOR_NAME + export GIT_AUTHOR_EMAIL + export GIT_AUTHOR_DATE + + git-cat-file commit $commit | sed -e '1,/^$/d' + 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 + +# 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). + +echo >&2 "First trying simple merge strategy to $me." +git-read-tree -m -u $base $head $next && +result=$(git-write-tree 2>/dev/null) || { + echo >&2 "Simple $me fails; trying Automatic $me." + git-merge-cache -o git-merge-one-file-script -a || { + echo >&2 "Automatic $me failed. After fixing it up," + echo >&2 "you can use \"git commit -F .msg\"" + 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 + } + result=$(git-write-tree) || exit +} +echo >&2 "Finished one $me." + +# 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 -F .msg + rm -f .msg + ;; +esac |