Merge branch 'rr/rebase-autostash-fix'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 17:06:57 +0000 (10:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 17:06:57 +0000 (10:06 -0700)
* rr/rebase-autostash-fix:
rebase -i: test "Nothing to do" case with autostash
rebase -i: handle "Nothing to do" case with autostash

1  2 
git-rebase--interactive.sh
git-rebase.sh
index 6ec9d3cb40b1e96f75132a11eb625fd42e64885d,85f0e93eee1400ba19d36caa75904435297a3f04..f267d8b6c3eea5c557e63814e40a9ef38b776354
@@@ -1,8 -1,11 +1,8 @@@
 -#!/bin/sh
 +# This shell script fragment is sourced by git-rebase to implement
 +# its interactive mode.  "git rebase --interactive" makes it easy
 +# to fix up commits in the middle of a series and rearrange commits.
  #
  # Copyright (c) 2006 Johannes E. Schindelin
 -
 -# SHORT DESCRIPTION
 -#
 -# This script makes it easy to fix up commits in the middle of a series,
 -# and rearrange commits.
  #
  # The original idea comes from Eric W. Biederman, in
  # http://article.gmane.org/gmane.comp.version-control.git/22407
@@@ -77,18 -80,6 +77,18 @@@ amend="$state_dir"/amen
  rewritten_list="$state_dir"/rewritten-list
  rewritten_pending="$state_dir"/rewritten-pending
  
 +strategy_args=
 +if test -n "$do_merge"
 +then
 +      strategy_args=${strategy:+--strategy=$strategy}
 +      eval '
 +              for strategy_opt in '"$strategy_opts"'
 +              do
 +                      strategy_args="$strategy_args -X$(git rev-parse --sq-quote "${strategy_opt#--}")"
 +              done
 +      '
 +fi
 +
  GIT_CHERRY_PICK_HELP="$resolvemsg"
  export GIT_CHERRY_PICK_HELP
  
@@@ -179,10 -170,9 +179,10 @@@ exit_with_patch () 
        echo "$1" > "$state_dir"/stopped-sha
        make_patch $1
        git rev-parse --verify HEAD > "$amend"
 +      gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
        warn "You can amend the commit now, with"
        warn
 -      warn "  git commit --amend"
 +      warn "  git commit --amend $gpg_sign_opt_quoted"
        warn
        warn "Once you are satisfied with your changes, run"
        warn
@@@ -249,9 -239,7 +249,9 @@@ pick_one () 
  
        test -d "$rewritten" &&
                pick_one_preserving_merges "$@" && return
 -      output git cherry-pick $empty_args $ff "$@"
 +      output eval git cherry-pick \
 +                      ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
 +                      "$strategy_args" $empty_args $ff "$@"
  }
  
  pick_one_preserving_merges () {
                        msg_content="$(commit_message $sha1)"
                        # No point in merging the first parent, that's HEAD
                        new_parents=${new_parents# $first_parent}
 -                      if ! do_with_author output \
 -                              git merge --no-ff ${strategy:+-s $strategy} -m \
 -                                      "$msg_content" $new_parents
 +                      merge_args="--no-log --no-ff"
 +                      if ! do_with_author output eval \
 +                      'git merge ${gpg_sign_opt:+"$gpg_sign_opt"} \
 +                              $merge_args $strategy_args -m "$msg_content" $new_parents'
                        then
                                printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
                                die_with_patch $sha1 "Error redoing merge $sha1"
                        echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
                        ;;
                *)
 -                      output git cherry-pick "$@" ||
 +                      output eval git cherry-pick \
 +                              ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
 +                              "$strategy_args" "$@" ||
                                die_with_patch $sha1 "Could not pick $sha1"
                        ;;
                esac
@@@ -476,8 -461,7 +476,8 @@@ do_pick () 
                           --no-post-rewrite -n -q -C $1 &&
                        pick_one -n $1 &&
                        git commit --allow-empty --allow-empty-message \
 -                                 --amend --no-post-rewrite -n -q -C $1 ||
 +                                 --amend --no-post-rewrite -n -q -C $1 \
 +                                 ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                        die_with_patch $1 "Could not apply $1... $2"
        else
                pick_one $1 ||
@@@ -504,7 -488,7 +504,7 @@@ do_next () 
  
                mark_action_done
                do_pick $sha1 "$rest"
 -              git commit --amend --no-post-rewrite || {
 +              git commit --amend --no-post-rewrite ${gpg_sign_opt:+"$gpg_sign_opt"} || {
                        warn "Could not amend commit after successfully picking $sha1... $rest"
                        warn "This is most likely due to an empty commit message, or the pre-commit hook"
                        warn "failed. If the pre-commit hook failed, you may need to resolve the issue before"
                squash|s|fixup|f)
                        # This is an intermediate commit; its message will only be
                        # used in case of trouble.  So use the long version:
 -                      do_with_author output git commit --amend --no-verify -F "$squash_msg" ||
 +                      do_with_author output git commit --amend --no-verify -F "$squash_msg" \
 +                              ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                die_failed_squash $sha1 "$rest"
                        ;;
                *)
                        # This is the final command of this squash/fixup group
                        if test -f "$fixup_msg"
                        then
 -                              do_with_author git commit --amend --no-verify -F "$fixup_msg" ||
 +                              do_with_author git commit --amend --no-verify -F "$fixup_msg" \
 +                                      ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                        die_failed_squash $sha1 "$rest"
                        else
                                cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
                                rm -f "$GIT_DIR"/MERGE_MSG
 -                              do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e ||
 +                              do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e \
 +                                      ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                        die_failed_squash $sha1 "$rest"
                        fi
                        rm -f "$squash_msg" "$fixup_msg"
@@@ -679,7 -660,7 +679,7 @@@ skip_unnecessary_picks () 
                                ;;
                        esac
                        ;;
 -              3,#*|3,)
 +              3,"$comment_char"*|3,)
                        # copy comments
                        ;;
                *)
        die "Could not skip unnecessary pick commands"
  }
  
 +transform_todo_ids () {
 +      while read -r command rest
 +      do
 +              case "$command" in
 +              "$comment_char"* | exec)
 +                      # Be careful for oddball commands like 'exec'
 +                      # that do not have a SHA-1 at the beginning of $rest.
 +                      ;;
 +              *)
 +                      sha1=$(git rev-parse --verify --quiet "$@" ${rest%% *}) &&
 +                      rest="$sha1 ${rest#* }"
 +                      ;;
 +              esac
 +              printf '%s\n' "$command${rest:+ }$rest"
 +      done <"$todo" >"$todo.new" &&
 +      mv -f "$todo.new" "$todo"
 +}
 +
 +expand_todo_ids() {
 +      transform_todo_ids
 +}
 +
 +collapse_todo_ids() {
 +      transform_todo_ids --short
 +}
 +
  # Rearrange the todo list that has both "pick sha1 msg" and
  # "pick sha1 fixup!/squash! msg" appears in it so that the latter
  # comes immediately after the former, and change "pick" to
@@@ -734,22 -689,8 +734,22 @@@ rearrange_squash () 
                case "$message" in
                "squash! "*|"fixup! "*)
                        action="${message%%!*}"
 -                      rest="${message#*! }"
 -                      echo "$sha1 $action $rest"
 +                      rest=$message
 +                      prefix=
 +                      # skip all squash! or fixup! (but save for later)
 +                      while :
 +                      do
 +                              case "$rest" in
 +                              "squash! "*|"fixup! "*)
 +                                      prefix="$prefix${rest%%!*},"
 +                                      rest="${rest#*! }"
 +                                      ;;
 +                              *)
 +                                      break
 +                                      ;;
 +                              esac
 +                      done
 +                      printf '%s %s %s %s\n' "$sha1" "$action" "$prefix" "$rest"
                        # if it's a single word, try to resolve to a full sha1 and
                        # emit a second copy. This allows us to match on both message
                        # and on sha1 prefix
                                if test -n "$fullsha"; then
                                        # prefix the action to uniquely identify this line as
                                        # intended for full sha1 match
 -                                      echo "$sha1 +$action $fullsha"
 +                                      echo "$sha1 +$action $prefix $fullsha"
                                fi
                        fi
                esac
                esac
                printf '%s\n' "$pick $sha1 $message"
                used="$used$sha1 "
 -              while read -r squash action msg_content
 +              while read -r squash action msg_prefix msg_content
                do
                        case " $used" in
                        *" $squash "*) continue ;;
                                case "$message" in "$msg_content"*) emit=1;; esac ;;
                        esac
                        if test $emit = 1; then
 -                              printf '%s\n' "$action $squash $action! $msg_content"
 +                              real_prefix=$(echo "$msg_prefix" | sed "s/,/! /g")
 +                              printf '%s\n' "$action $squash ${real_prefix}$msg_content"
                                used="$used$squash "
                        fi
                done <"$1.sq"
@@@ -820,17 -760,6 +820,17 @@@ add_exec_commands () 
        mv "$1.new" "$1"
  }
  
 +# The whole contents of this file is run by dot-sourcing it from
 +# inside a shell function.  It used to be that "return"s we see
 +# below were not inside any function, and expected to return
 +# to the function that dot-sourced us.
 +#
 +# However, FreeBSD /bin/sh misbehaves on such a construct and
 +# continues to run the statements that follow such a "return".
 +# As a work-around, we introduce an extra layer of a function
 +# here, and immediately call it after defining it.
 +git_rebase__interactive () {
 +
  case "$action" in
  continue)
        # do we have anything to commit?
        else
                if ! test -f "$author_script"
                then
 +                      gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
                        die "You have staged changes in your working tree. If these changes are meant to be
  squashed into the previous commit, run:
  
 -  git commit --amend
 +  git commit --amend $gpg_sign_opt_quoted
  
  If they are meant to go into a new commit, run:
  
 -  git commit
 +  git commit $gpg_sign_opt_quoted
  
  In both case, once you're done, continue with:
  
                        die "\
  You have uncommitted changes in your working tree. Please, commit them
  first and then run 'git rebase --continue' again."
 -                      do_with_author git commit --amend --no-verify -F "$msg" -e ||
 +                      do_with_author git commit --amend --no-verify -F "$msg" -e \
 +                              ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                die "Could not commit staged changes."
                else
 -                      do_with_author git commit --no-verify -F "$msg" -e ||
 +                      do_with_author git commit --no-verify -F "$msg" -e \
 +                              ${gpg_sign_opt:+"$gpg_sign_opt"} ||
                                die "Could not commit staged changes."
                fi
        fi
@@@ -889,7 -815,6 +889,7 @@@ skip
  edit-todo)
        git stripspace --strip-comments <"$todo" >"$todo".new
        mv -f "$todo".new "$todo"
 +      collapse_todo_ids
        append_todo_help
        git stripspace --comment-lines >>"$todo" <<\EOF
  
@@@ -901,7 -826,6 +901,7 @@@ EO
  
        git_sequence_editor "$todo" ||
                die "Could not execute editor"
 +      expand_todo_ids
  
        exit
        ;;
@@@ -914,11 -838,8 +914,11 @@@ comment_for_reflog star
  
  if test ! -z "$switch_to"
  then
 +      GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
        output git checkout "$switch_to" -- ||
 -              die "Could not checkout $switch_to"
 +      die "Could not checkout $switch_to"
 +
 +      comment_for_reflog start
  fi
  
  orig_head=$(git rev-parse --verify HEAD) || die "No HEAD?"
  
  
  has_action "$todo" ||
-       die_abort "Nothing to do"
+       return 2
  
  cp "$todo" "$todo".backup
  git_sequence_editor "$todo" ||
        die_abort "Could not execute editor"
  
  has_action "$todo" ||
-       die_abort "Nothing to do"
+       return 2
  
 +expand_todo_ids
 +
  test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
  
 +GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
  output git checkout $onto || die_abort "could not detach HEAD"
  git update-ref ORIG_HEAD $orig_head
  do_rest
 +
 +}
 +# ... and then we call the whole thing.
 +git_rebase__interactive
diff --combined git-rebase.sh
index 5c7a0a1a583552de49ee2e094e80f80ee18fd68c,9eb276ba203ae552d1b64fe49814b7e8d8f91e1b..06c810b64fe04d3675820e38d7eeabe7589dab56
@@@ -5,7 -5,6 +5,7 @@@
  
  SUBDIRECTORY_OK=Yes
  OPTIONS_KEEPDASHDASH=
 +OPTIONS_STUCKLONG=t
  OPTIONS_SPEC="\
  git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
  git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
@@@ -15,7 -14,6 +15,7 @@@ git-rebase --continue | --abort | --ski
  v,verbose!         display a diffstat of what changed upstream
  q,quiet!           be quiet. implies --no-stat
  autostash!         automatically stash/stash pop before and after
 +fork-point         use 'merge-base --fork-point' to refine upstream
  onto=!             rebase onto given branch instead of upstream
  p,preserve-merges! try to recreate merges instead of ignoring them
  s,strategy=!       use the given merge strategy
@@@ -37,7 -35,6 +37,7 @@@ ignore-date!       passed to 'git am
  whitespace=!       passed to 'git apply'
  ignore-whitespace! passed to 'git apply'
  C=!                passed to 'git apply'
 +S,gpg-sign?        GPG-sign commits
   Actions:
  continue!          continue
  abort!             abort and check out the original branch
@@@ -69,7 -66,6 +69,7 @@@ verbose
  diffstat=
  test "$(git config --bool rebase.stat)" = true && diffstat=t
  autostash="$(git config --bool rebase.autostash || echo false)"
 +fork_point=auto
  git_am_opt=
  rebase_root=
  force_rebase=
@@@ -86,7 -82,6 +86,7 @@@ preserve_merges
  autosquash=
  keep_empty=
  test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
 +gpg_sign_opt=
  
  read_basic_state () {
        test -f "$state_dir/head-name" &&
                strategy_opts="$(cat "$state_dir"/strategy_opts)"
        test -f "$state_dir"/allow_rerere_autoupdate &&
                allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
 +      test -f "$state_dir"/gpg_sign_opt &&
 +              gpg_sign_opt="$(cat "$state_dir"/gpg_sign_opt)"
  }
  
  write_basic_state () {
                "$state_dir"/strategy_opts
        test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
                "$state_dir"/allow_rerere_autoupdate
 +      test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt
  }
  
  output () {
@@@ -155,7 -147,7 +155,7 @@@ move_to_original_branch () 
        esac
  }
  
finish_rebase () {
apply_autostash () {
        if test -f "$state_dir/autostash"
        then
                stash_sha1=$(cat "$state_dir/autostash")
                then
                        echo "$(gettext 'Applied autostash.')"
                else
 -                      ref_stash=refs/stash &&
 -                      >>"$GIT_DIR/logs/$ref_stash" &&
 -                      git update-ref -m "autostash" $ref_stash $stash_sha1 ||
 -                      die "$(eval_gettext 'Cannot store $stash_sha1')"
 -
 +                      git stash store -m "autostash" -q $stash_sha1 ||
 +                      die "$(eval_gettext "Cannot store \$stash_sha1")"
                        gettext 'Applying autostash resulted in conflicts.
  Your changes are safe in the stash.
  You can run "git stash pop" or "git stash drop" at any time.
  '
                fi
        fi
+ }
+ finish_rebase () {
+       apply_autostash &&
        git gc --auto &&
        rm -rf "$state_dir"
  }
@@@ -186,6 -185,11 +190,11 @@@ run_specific_rebase () 
        if test $ret -eq 0
        then
                finish_rebase
+       elif test $ret -eq 2 # special exit status for rebase -i
+       then
+               apply_autostash &&
+               rm -rf "$state_dir" &&
+               die "Nothing to do"
        fi
        exit $ret
  }
                test $total_argc -eq 2 || usage
                action=${1##--}
                ;;
 -      --onto)
 -              test 2 -le "$#" || usage
 -              onto="$2"
 -              shift
 +      --onto=*)
 +              onto="${1#--onto=}"
                ;;
 -      -x)
 -              test 2 -le "$#" || usage
 -              cmd="${cmd}exec $2${LF}"
 -              shift
 +      --exec=*)
 +              cmd="${cmd}exec ${1#--exec=}${LF}"
                ;;
 -      -i)
 +      --interactive)
                interactive_rebase=explicit
                ;;
 -      -k)
 +      --keep-empty)
                keep_empty=yes
                ;;
 -      -p)
 +      --preserve-merges)
                preserve_merges=t
                test -z "$interactive_rebase" && interactive_rebase=implied
                ;;
        --no-autosquash)
                autosquash=
                ;;
 -      -M|-m)
 +      --fork-point)
 +              fork_point=t
 +              ;;
 +      --no-fork-point)
 +              fork_point=
 +              ;;
 +      --merge)
                do_merge=t
                ;;
 -      -X)
 -              shift
 -              strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$1")"
 +      --strategy-option=*)
 +              strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}")"
                do_merge=t
                test -z "$strategy" && strategy=recursive
                ;;
 -      -s)
 -              shift
 -              strategy="$1"
 +      --strategy=*)
 +              strategy="${1#--strategy=}"
                do_merge=t
                ;;
 -      -n)
 +      --no-stat)
                diffstat=
                ;;
        --stat)
        --autostash)
                autostash=true
                ;;
 -      -v)
 +      --verbose)
                verbose=t
                diffstat=t
                GIT_QUIET=
                ;;
 -      -q)
 +      --quiet)
                GIT_QUIET=t
                git_am_opt="$git_am_opt -q"
                verbose=
                diffstat=
                ;;
 -      --whitespace)
 -              shift
 -              git_am_opt="$git_am_opt --whitespace=$1"
 -              case "$1" in
 +      --whitespace=*)
 +              git_am_opt="$git_am_opt --whitespace=${1#--whitespace=}"
 +              case "${1#--whitespace=}" in
                fix|strip)
                        force_rebase=t
                        ;;
                git_am_opt="$git_am_opt $1"
                force_rebase=t
                ;;
 -      -C)
 -              shift
 -              git_am_opt="$git_am_opt -C$1"
 +      -C*)
 +              git_am_opt="$git_am_opt $1"
                ;;
        --root)
                rebase_root=t
                ;;
 -      -f|--no-ff)
 +      --force-rebase|--no-ff)
                force_rebase=t
                ;;
        --rerere-autoupdate|--no-rerere-autoupdate)
                allow_rerere_autoupdate="$1"
                ;;
 +      --gpg-sign)
 +              gpg_sign_opt=-S
 +              ;;
 +      --gpg-sign=*)
 +              gpg_sign_opt="-S${1#--gpg-sign=}"
 +              ;;
        --)
                shift
                break
@@@ -440,25 -440,19 +449,25 @@@ the
                        error_on_missing_default_upstream "rebase" "rebase" \
                                "against" "git rebase <branch>"
                fi
 +
 +              test "$fork_point" = auto && fork_point=t
                ;;
        *)      upstream_name="$1"
 +              if test "$upstream_name" = "-"
 +              then
 +                      upstream_name="@{-1}"
 +              fi
                shift
                ;;
        esac
 -      upstream=`git rev-parse --verify "${upstream_name}^0"` ||
 +      upstream=$(peel_committish "${upstream_name}") ||
        die "$(eval_gettext "invalid upstream \$upstream_name")"
        upstream_arg="$upstream_name"
  else
        if test -z "$onto"
        then
 -              empty_tree=`git hash-object -t tree /dev/null`
 -              onto=`git commit-tree $empty_tree </dev/null`
 +              empty_tree=$(git hash-object -t tree /dev/null)
 +              onto=$(git commit-tree $empty_tree </dev/null)
                squash_onto="$onto"
        fi
        unset upstream_name
@@@ -487,7 -481,7 +496,7 @@@ case "$onto_name" i
        fi
        ;;
  *)
 -      onto=$(git rev-parse --verify "${onto_name}^0") ||
 +      onto=$(peel_committish "$onto_name") ||
        die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
        ;;
  esac
@@@ -516,10 -510,10 +525,10 @@@ case "$#" i
        ;;
  0)
        # Do not need to switch branches, we are already on it.
 -      if branch_name=`git symbolic-ref -q HEAD`
 +      if branch_name=$(git symbolic-ref -q HEAD)
        then
                head_name=$branch_name
 -              branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
 +              branch_name=$(expr "z$branch_name" : 'zrefs/heads/\(.*\)')
        else
                head_name="detached HEAD"
                branch_name=HEAD ;# detached
        ;;
  esac
  
 +if test "$fork_point" = t
 +then
 +      new_upstream=$(git merge-base --fork-point "$upstream_name" \
 +                      "${switch_to:-HEAD}")
 +      if test -n "$new_upstream"
 +      then
 +              upstream=$new_upstream
 +      fi
 +fi
 +
  if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
  then
        stash_sha1=$(git stash create "autostash") ||
@@@ -570,9 -554,7 +579,9 @@@ the
        if test -z "$force_rebase"
        then
                # Lazily switch to the target branch if needed...
 -              test -z "$switch_to" || git checkout "$switch_to" --
 +              test -z "$switch_to" ||
 +              GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
 +                      git checkout "$switch_to" --
                say "$(eval_gettext "Current branch \$branch_name is up to date.")"
                finish_rebase
                exit 0
@@@ -598,9 -580,7 +607,9 @@@ test "$type" = interactive && run_speci
  
  # Detach HEAD and reset the tree
  say "$(gettext "First, rewinding head to replay your work on top of it...")"
 -git checkout -q "$onto^0" || die "could not detach HEAD"
 +
 +GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
 +      git checkout -q "$onto^0" || die "could not detach HEAD"
  git update-ref ORIG_HEAD $orig_head
  
  # If the $onto is a proper descendant of the tip of the branch, then