Code

rebase -i: more graceful handling of invalid commands
[git.git] / git-rebase--interactive.sh
index 59c148ff6d477c5c40258d3adc5c95f5c775ccc6..9b1e899e279e63cf3b952d910974ebf619d63bee 100755 (executable)
@@ -26,6 +26,8 @@ i,interactive      always used (no-op)
 continue           continue rebasing process
 abort              abort rebasing process and restore original branch
 skip               skip current patch and continue rebasing process
 continue           continue rebasing process
 abort              abort rebasing process and restore original branch
 skip               skip current patch and continue rebasing process
+no-verify          override pre-rebase hook from stopping the operation
+root               rebase all reachable commmits up to the root(s)
 "
 
 . git-sh-setup
 "
 
 . git-sh-setup
@@ -37,10 +39,13 @@ DONE="$DOTEST"/done
 MSG="$DOTEST"/message
 SQUASH_MSG="$DOTEST"/message-squash
 REWRITTEN="$DOTEST"/rewritten
 MSG="$DOTEST"/message
 SQUASH_MSG="$DOTEST"/message-squash
 REWRITTEN="$DOTEST"/rewritten
+DROPPED="$DOTEST"/dropped
 PRESERVE_MERGES=
 STRATEGY=
 ONTO=
 VERBOSE=
 PRESERVE_MERGES=
 STRATEGY=
 ONTO=
 VERBOSE=
+OK_TO_SKIP_PRE_REBASE=
+REBASE_ROOT=
 
 GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
 mark the corrected paths with 'git add <paths>', and
 
 GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
 mark the corrected paths with 'git add <paths>', and
@@ -66,7 +71,8 @@ output () {
 }
 
 run_pre_rebase_hook () {
 }
 
 run_pre_rebase_hook () {
-       if test -x "$GIT_DIR/hooks/pre-rebase"
+       if test -z "$OK_TO_SKIP_PRE_REBASE" &&
+          test -x "$GIT_DIR/hooks/pre-rebase"
        then
                "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
                        echo >&2 "The pre-rebase hook refused to rebase."
        then
                "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
                        echo >&2 "The pre-rebase hook refused to rebase."
@@ -111,9 +117,18 @@ mark_action_done () {
 }
 
 make_patch () {
 }
 
 make_patch () {
-       parent_sha1=$(git rev-parse --verify "$1"^) ||
-               die "Cannot get patch for $1^"
-       git diff-tree -p "$parent_sha1".."$1" > "$DOTEST"/patch
+       sha1_and_parents="$(git rev-list --parents -1 "$1")"
+       case "$sha1_and_parents" in
+       ?*' '?*' '?*)
+               git diff --cc $sha1_and_parents
+               ;;
+       ?*' '?*)
+               git diff-tree -p "$1^!"
+               ;;
+       *)
+               echo "Root commit"
+               ;;
+       esac > "$DOTEST"/patch
        test -f "$DOTEST"/message ||
                git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message
        test -f "$DOTEST"/author-script ||
        test -f "$DOTEST"/message ||
                git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message
        test -f "$DOTEST"/author-script ||
@@ -141,6 +156,11 @@ pick_one () {
        output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
        test -d "$REWRITTEN" &&
                pick_one_preserving_merges "$@" && return
        output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
        test -d "$REWRITTEN" &&
                pick_one_preserving_merges "$@" && return
+       if test ! -z "$REBASE_ROOT"
+       then
+               output git cherry-pick "$@"
+               return
+       fi
        parent_sha1=$(git rev-parse --verify $sha1^) ||
                die "Could not get the parent of $sha1"
        current_sha1=$(git rev-parse --verify HEAD)
        parent_sha1=$(git rev-parse --verify $sha1^) ||
                die "Could not get the parent of $sha1"
        current_sha1=$(git rev-parse --verify HEAD)
@@ -169,21 +189,43 @@ pick_one_preserving_merges () {
 
        if test -f "$DOTEST"/current-commit
        then
 
        if test -f "$DOTEST"/current-commit
        then
-               current_commit=$(cat "$DOTEST"/current-commit) &&
-               git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
-               rm "$DOTEST"/current-commit ||
-               die "Cannot write current commit's replacement sha1"
+               if test "$fast_forward" = t
+               then
+                       cat "$DOTEST"/current-commit | while read current_commit
+                       do
+                               git rev-parse HEAD > "$REWRITTEN"/$current_commit
+                       done
+                       rm "$DOTEST"/current-commit ||
+                       die "Cannot write current commit's replacement sha1"
+               fi
        fi
 
        fi
 
-       echo $sha1 > "$DOTEST"/current-commit
+       echo $sha1 >> "$DOTEST"/current-commit
 
        # rewrite parents; if none were rewritten, we can fast-forward.
        new_parents=
 
        # rewrite parents; if none were rewritten, we can fast-forward.
        new_parents=
-       for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
+       pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
+       if test "$pend" = " "
+       then
+               pend=" root"
+       fi
+       while [ "$pend" != "" ]
        do
        do
+               p=$(expr "$pend" : ' \([^ ]*\)')
+               pend="${pend# $p}"
+
                if test -f "$REWRITTEN"/$p
                then
                        new_p=$(cat "$REWRITTEN"/$p)
                if test -f "$REWRITTEN"/$p
                then
                        new_p=$(cat "$REWRITTEN"/$p)
+
+                       # If the todo reordered commits, and our parent is marked for
+                       # rewriting, but hasn't been gotten to yet, assume the user meant to
+                       # drop it on top of the current HEAD
+                       if test -z "$new_p"
+                       then
+                               new_p=$(git rev-parse HEAD)
+                       fi
+
                        test $p != $new_p && fast_forward=f
                        case "$new_parents" in
                        *$new_p*)
                        test $p != $new_p && fast_forward=f
                        case "$new_parents" in
                        *$new_p*)
@@ -193,7 +235,15 @@ pick_one_preserving_merges () {
                                ;;
                        esac
                else
                                ;;
                        esac
                else
-                       new_parents="$new_parents $p"
+                       if test -f "$DROPPED"/$p
+                       then
+                               fast_forward=f
+                               replacement="$(cat "$DROPPED"/$p)"
+                               test -z "$replacement" && replacement=root
+                               pend=" $replacement$pend"
+                       else
+                               new_parents="$new_parents $p"
+                       fi
                fi
        done
        case $fast_forward in
                fi
        done
        case $fast_forward in
@@ -203,15 +253,19 @@ pick_one_preserving_merges () {
                        die "Cannot fast forward to $sha1"
                ;;
        f)
                        die "Cannot fast forward to $sha1"
                ;;
        f)
-               test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
-
                first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
                first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
-               # detach HEAD to current parent
-               output git checkout $first_parent 2> /dev/null ||
-                       die "Cannot move HEAD to $first_parent"
+
+               if [ "$1" != "-n" ]
+               then
+                       # detach HEAD to current parent
+                       output git checkout $first_parent 2> /dev/null ||
+                               die "Cannot move HEAD to $first_parent"
+               fi
 
                case "$new_parents" in
                ' '*' '*)
 
                case "$new_parents" in
                ' '*' '*)
+                       test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
+
                        # redo merge
                        author_script=$(get_author_ident_from_commit $sha1)
                        eval "$author_script"
                        # redo merge
                        author_script=$(get_author_ident_from_commit $sha1)
                        eval "$author_script"
@@ -224,9 +278,8 @@ pick_one_preserving_merges () {
                                output git merge $STRATEGY -m "$msg" \
                                        $new_parents
                        then
                                output git merge $STRATEGY -m "$msg" \
                                        $new_parents
                        then
-                               git rerere
                                printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
                                printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
-                               die Error redoing merge $sha1
+                               die_with_patch $sha1 "Error redoing merge $sha1"
                        fi
                        ;;
                *)
                        fi
                        ;;
                *)
@@ -320,17 +373,15 @@ do_next () {
                pick_one -n $sha1 || failed=t
                case "$(peek_next_command)" in
                squash|s)
                pick_one -n $sha1 || failed=t
                case "$(peek_next_command)" in
                squash|s)
-                       EDIT_COMMIT=
                        USE_OUTPUT=output
                        MSG_OPT=-F
                        USE_OUTPUT=output
                        MSG_OPT=-F
-                       MSG_FILE="$MSG"
+                       EDIT_OR_FILE="$MSG"
                        cp "$MSG" "$SQUASH_MSG"
                        ;;
                *)
                        cp "$MSG" "$SQUASH_MSG"
                        ;;
                *)
-                       EDIT_COMMIT=-e
                        USE_OUTPUT=
                        MSG_OPT=
                        USE_OUTPUT=
                        MSG_OPT=
-                       MSG_FILE=
+                       EDIT_OR_FILE=-e
                        rm -f "$SQUASH_MSG" || exit
                        cp "$MSG" "$GIT_DIR"/SQUASH_MSG
                        rm -f "$GIT_DIR"/MERGE_MSG || exit
                        rm -f "$SQUASH_MSG" || exit
                        cp "$MSG" "$GIT_DIR"/SQUASH_MSG
                        rm -f "$GIT_DIR"/MERGE_MSG || exit
@@ -344,7 +395,8 @@ do_next () {
                        GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
                        GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
                        GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
                        GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
                        GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
                        GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
-                       $USE_OUTPUT git commit --no-verify $MSG_OPT "$MSG_FILE" $EDIT_COMMIT || failed=t
+                       $USE_OUTPUT git commit --no-verify \
+                               $MSG_OPT "$EDIT_OR_FILE" || failed=t
                fi
                if test $failed = t
                then
                fi
                if test $failed = t
                then
@@ -356,7 +408,12 @@ do_next () {
                ;;
        *)
                warn "Unknown command: $command $sha1 $rest"
                ;;
        *)
                warn "Unknown command: $command $sha1 $rest"
-               die_with_patch $sha1 "Please fix this in the file $TODO."
+               if git rev-parse --verify -q "$sha1" >/dev/null
+               then
+                       die_with_patch $sha1 "Please fix this in the file $TODO."
+               else
+                       die "Please fix this in the file $TODO."
+               fi
                ;;
        esac
        test -s "$TODO" && return
                ;;
        esac
        test -s "$TODO" && return
@@ -365,23 +422,10 @@ do_next () {
        HEADNAME=$(cat "$DOTEST"/head-name) &&
        OLDHEAD=$(cat "$DOTEST"/head) &&
        SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
        HEADNAME=$(cat "$DOTEST"/head-name) &&
        OLDHEAD=$(cat "$DOTEST"/head) &&
        SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
-       if test -d "$REWRITTEN"
-       then
-               test -f "$DOTEST"/current-commit &&
-                       current_commit=$(cat "$DOTEST"/current-commit) &&
-                       git rev-parse HEAD > "$REWRITTEN"/$current_commit
-               if test -f "$REWRITTEN"/$OLDHEAD
-               then
-                       NEWHEAD=$(cat "$REWRITTEN"/$OLDHEAD)
-               else
-                       NEWHEAD=$OLDHEAD
-               fi
-       else
-               NEWHEAD=$(git rev-parse HEAD)
-       fi &&
+       NEWHEAD=$(git rev-parse HEAD) &&
        case $HEADNAME in
        refs/*)
        case $HEADNAME in
        refs/*)
-               message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
+               message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" &&
                git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
                git symbolic-ref HEAD $HEADNAME
                ;;
                git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
                git symbolic-ref HEAD $HEADNAME
                ;;
@@ -403,6 +447,30 @@ do_rest () {
        done
 }
 
        done
 }
 
+# skip picking commits whose parents are unchanged
+skip_unnecessary_picks () {
+       fd=3
+       while read command sha1 rest
+       do
+               # fd=3 means we skip the command
+               case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in
+               3,pick,"$ONTO"*|3,p,"$ONTO"*)
+                       # pick a commit whose parent is current $ONTO -> skip
+                       ONTO=$sha1
+                       ;;
+               3,#*|3,,*)
+                       # copy comments
+                       ;;
+               *)
+                       fd=1
+                       ;;
+               esac
+               echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
+       done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
+       mv -f "$TODO".new "$TODO" ||
+       die "Could not skip unnecessary pick commands"
+}
+
 # check if no other options are set
 is_standalone () {
        test $# -eq 2 -a "$2" = '--' &&
 # check if no other options are set
 is_standalone () {
        test $# -eq 2 -a "$2" = '--' &&
@@ -416,11 +484,17 @@ get_saved_options () {
        test -d "$REWRITTEN" && PRESERVE_MERGES=t
        test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
        test -f "$DOTEST"/verbose && VERBOSE=t
        test -d "$REWRITTEN" && PRESERVE_MERGES=t
        test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
        test -f "$DOTEST"/verbose && VERBOSE=t
+       test -f "$DOTEST"/rebase-root && REBASE_ROOT=t
 }
 
 while test $# != 0
 do
        case "$1" in
 }
 
 while test $# != 0
 do
        case "$1" in
+       --no-verify)
+               OK_TO_SKIP_PRE_REBASE=yes
+               ;;
+       --verify)
+               ;;
        --continue)
                is_standalone "$@" || usage
                get_saved_options
        --continue)
                is_standalone "$@" || usage
                get_saved_options
@@ -515,6 +589,9 @@ first and then run 'git rebase --continue' again."
        -i)
                # yeah, we know
                ;;
        -i)
                # yeah, we know
                ;;
+       --root)
+               REBASE_ROOT=t
+               ;;
        --onto)
                shift
                ONTO=$(git rev-parse --verify "$1") ||
        --onto)
                shift
                ONTO=$(git rev-parse --verify "$1") ||
@@ -522,27 +599,38 @@ first and then run 'git rebase --continue' again."
                ;;
        --)
                shift
                ;;
        --)
                shift
-               run_pre_rebase_hook ${1+"$@"}
-               test $# -eq 1 -o $# -eq 2 || usage
+               test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 ||
+               test ! -z "$REBASE_ROOT" -a $# -le 1 || usage
                test -d "$DOTEST" &&
                        die "Interactive rebase already started"
 
                git var GIT_COMMITTER_IDENT >/dev/null ||
                        die "You need to set your committer info first"
 
                test -d "$DOTEST" &&
                        die "Interactive rebase already started"
 
                git var GIT_COMMITTER_IDENT >/dev/null ||
                        die "You need to set your committer info first"
 
+               if test -z "$REBASE_ROOT"
+               then
+                       UPSTREAM_ARG="$1"
+                       UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
+                       test -z "$ONTO" && ONTO=$UPSTREAM
+                       shift
+               else
+                       UPSTREAM=
+                       UPSTREAM_ARG=--root
+                       test -z "$ONTO" &&
+                               die "You must specify --onto when using --root"
+               fi
+               run_pre_rebase_hook "$UPSTREAM_ARG" "$@"
+
                comment_for_reflog start
 
                require_clean_work_tree
 
                comment_for_reflog start
 
                require_clean_work_tree
 
-               UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
-               test -z "$ONTO" && ONTO=$UPSTREAM
-
-               if test ! -z "$2"
+               if test ! -z "$1"
                then
                then
-                       output git show-ref --verify --quiet "refs/heads/$2" ||
-                               die "Invalid branchname: $2"
-                       output git checkout "$2" ||
-                               die "Could not checkout $2"
+                       output git show-ref --verify --quiet "refs/heads/$1" ||
+                               die "Invalid branchname: $1"
+                       output git checkout "$1" ||
+                               die "Could not checkout $1"
                fi
 
                HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
                fi
 
                HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
@@ -553,7 +641,12 @@ first and then run 'git rebase --continue' again."
                        echo "detached HEAD" > "$DOTEST"/head-name
 
                echo $HEAD > "$DOTEST"/head
                        echo "detached HEAD" > "$DOTEST"/head-name
 
                echo $HEAD > "$DOTEST"/head
-               echo $UPSTREAM > "$DOTEST"/upstream
+               case "$REBASE_ROOT" in
+               '')
+                       rm -f "$DOTEST"/rebase-root ;;
+               *)
+                       : >"$DOTEST"/rebase-root ;;
+               esac
                echo $ONTO > "$DOTEST"/onto
                test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
                test t = "$VERBOSE" && : > "$DOTEST"/verbose
                echo $ONTO > "$DOTEST"/onto
                test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
                test t = "$VERBOSE" && : > "$DOTEST"/verbose
@@ -566,28 +659,101 @@ first and then run 'git rebase --continue' again."
                        # This ensures that commits on merged, but otherwise
                        # unrelated side branches are left alone. (Think "X"
                        # in the man page's example.)
                        # This ensures that commits on merged, but otherwise
                        # unrelated side branches are left alone. (Think "X"
                        # in the man page's example.)
-                       mkdir "$REWRITTEN" &&
-                       for c in $(git merge-base --all $HEAD $UPSTREAM)
-                       do
-                               echo $ONTO > "$REWRITTEN"/$c ||
+                       if test -z "$REBASE_ROOT"
+                       then
+                               mkdir "$REWRITTEN" &&
+                               for c in $(git merge-base --all $HEAD $UPSTREAM)
+                               do
+                                       echo $ONTO > "$REWRITTEN"/$c ||
+                                               die "Could not init rewritten commits"
+                               done
+                       else
+                               mkdir "$REWRITTEN" &&
+                               echo $ONTO > "$REWRITTEN"/root ||
                                        die "Could not init rewritten commits"
                                        die "Could not init rewritten commits"
-                       done
+                       fi
+                       # No cherry-pick because our first pass is to determine
+                       # parents to rewrite and skipping dropped commits would
+                       # prematurely end our probe
                        MERGES_OPTION=
                        MERGES_OPTION=
+                       first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)"
                else
                else
-                       MERGES_OPTION=--no-merges
+                       MERGES_OPTION="--no-merges --cherry-pick"
                fi
 
                fi
 
-               SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
                SHORTHEAD=$(git rev-parse --short $HEAD)
                SHORTONTO=$(git rev-parse --short $ONTO)
                SHORTHEAD=$(git rev-parse --short $HEAD)
                SHORTONTO=$(git rev-parse --short $ONTO)
+               if test -z "$REBASE_ROOT"
+                       # this is now equivalent to ! -z "$UPSTREAM"
+               then
+                       SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
+                       REVISIONS=$UPSTREAM...$HEAD
+                       SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD
+               else
+                       REVISIONS=$ONTO...$HEAD
+                       SHORTREVISIONS=$SHORTHEAD
+               fi
                git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
                git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
-                       --abbrev=7 --reverse --left-right --cherry-pick \
-                       $UPSTREAM...$HEAD | \
-                       sed -n "s/^>/pick /p" > "$TODO"
+                       --abbrev=7 --reverse --left-right --topo-order \
+                       $REVISIONS | \
+                       sed -n "s/^>//p" | while read shortsha1 rest
+               do
+                       if test t != "$PRESERVE_MERGES"
+                       then
+                               echo "pick $shortsha1 $rest" >> "$TODO"
+                       else
+                               sha1=$(git rev-parse $shortsha1)
+                               if test -z "$REBASE_ROOT"
+                               then
+                                       preserve=t
+                                       for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
+                                       do
+                                               if test -f "$REWRITTEN"/$p -a \( $p != $ONTO -o $sha1 = $first_after_upstream \)
+                                               then
+                                                       preserve=f
+                                               fi
+                                       done
+                               else
+                                       preserve=f
+                               fi
+                               if test f = "$preserve"
+                               then
+                                       touch "$REWRITTEN"/$sha1
+                                       echo "pick $shortsha1 $rest" >> "$TODO"
+                               fi
+                       fi
+               done
+
+               # Watch for commits that been dropped by --cherry-pick
+               if test t = "$PRESERVE_MERGES"
+               then
+                       mkdir "$DROPPED"
+                       # Save all non-cherry-picked changes
+                       git rev-list $REVISIONS --left-right --cherry-pick | \
+                               sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks
+                       # Now all commits and note which ones are missing in
+                       # not-cherry-picks and hence being dropped
+                       git rev-list $REVISIONS |
+                       while read rev
+                       do
+                               if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = ""
+                               then
+                                       # Use -f2 because if rev-list is telling us this commit is
+                                       # not worthwhile, we don't want to track its multiple heads,
+                                       # just the history of its first-parent for others that will
+                                       # be rebasing on top of it
+                                       git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev
+                                       short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev)
+                                       grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO"
+                                       rm "$REWRITTEN"/$rev
+                               fi
+                       done
+               fi
+
                test -s "$TODO" || echo noop >> "$TODO"
                cat >> "$TODO" << EOF
 
                test -s "$TODO" || echo noop >> "$TODO"
                cat >> "$TODO" << EOF
 
-# Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO
+# Rebase $SHORTREVISIONS onto $SHORTONTO
 #
 # Commands:
 #  p, pick = use commit
 #
 # Commands:
 #  p, pick = use commit
@@ -609,6 +775,8 @@ EOF
                has_action "$TODO" ||
                        die_abort "Nothing to do"
 
                has_action "$TODO" ||
                        die_abort "Nothing to do"
 
+               test -d "$REWRITTEN" || skip_unnecessary_picks
+
                git update-ref ORIG_HEAD $HEAD
                output git checkout $ONTO && do_rest
                ;;
                git update-ref ORIG_HEAD $HEAD
                output git checkout $ONTO && do_rest
                ;;