Merge branch 'ef/maint-rebase-error-message'
authorJunio C Hamano <gitster@pobox.com>
Fri, 1 Jun 2012 20:28:25 +0000 (13:28 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 1 Jun 2012 20:28:25 +0000 (13:28 -0700)
By Erik Faye-Lund
* ef/maint-rebase-error-message:
rebase: report invalid commit correctly

1  2 
git-rebase.sh
diff --combined git-rebase.sh
index 9148ec25c1c865f28a84e64ae67c85dca28f1eaf,5e3c42cb1150a6af18003db51e7d5cc1accb4842..e6167374445dfbb5e0b6bedc9a5f33a01cb5a7df
@@@ -3,7 -3,7 +3,7 @@@
  # Copyright (c) 2005 Junio C Hamano.
  #
  
 -USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] (<upstream>|--root) [<branch>] [--quiet | -q]'
 +USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
  LONG_USAGE='git-rebase replaces <branch> with a new branch of the
  same name.  When the --onto option is provided the new branch starts
  out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@@ -13,7 -13,7 +13,7 @@@ It then attempts to create a new commi
  It is possible that a merge failure will prevent this process from being
  completely automatic.  You will have to resolve any such merge failure
  and run git rebase --continue.  Another option is to bypass the commit
 -that caused the merge failure with git rebase --skip.  To restore the
 +that caused the merge failure with git rebase --skip.  To check out the
  original <branch> and remove the .git/rebase-apply working files, use the
  command git rebase --abort instead.
  
@@@ -22,49 -22,16 +22,49 @@@ currently checked out branch is used
  
  Example:       git-rebase master~1 topic
  
 -        A---B---C topic                   A'\''--B'\''--C'\'' topic
 +      A---B---C topic                   A'\''--B'\''--C'\'' topic
         /                   -->           /
    D---E---F---G master          D---E---F---G master
  '
  
  SUBDIRECTORY_OK=Yes
 -OPTIONS_SPEC=
 +OPTIONS_KEEPDASHDASH=
 +OPTIONS_SPEC="\
 +git rebase [-i] [options] [--onto <newbase>] [<upstream>] [<branch>]
 +git rebase [-i] [options] --onto <newbase> --root [<branch>]
 +git-rebase [-i] --continue | --abort | --skip
 +--
 + Available options are
 +v,verbose!         display a diffstat of what changed upstream
 +q,quiet!           be quiet. implies --no-stat
 +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
 +no-ff!             cherry-pick all commits, even if unchanged
 +m,merge!           use merging strategies to rebase
 +i,interactive!     let the user edit the list of commits to rebase
 +k,keep-empty     preserve empty commits during rebase
 +f,force-rebase!    force rebase even if branch is up to date
 +X,strategy-option=! pass the argument through to the merge strategy
 +stat!              display a diffstat of what changed upstream
 +n,no-stat!         do not show diffstat of what changed upstream
 +verify             allow pre-rebase hook to run
 +rerere-autoupdate  allow rerere to update index with resolved conflicts
 +root!              rebase all reachable commits up to the root(s)
 +autosquash         move commits that begin with squash!/fixup! under -i
 +committer-date-is-author-date! passed to 'git am'
 +ignore-date!       passed to 'git am'
 +whitespace=!       passed to 'git apply'
 +ignore-whitespace! passed to 'git apply'
 +C=!                passed to 'git apply'
 + Actions:
 +continue!          continue
 +abort!             abort and check out the original branch
 +skip!              skip current patch and continue
 +"
  . git-sh-setup
  set_reflog_action rebase
 -require_work_tree
 +require_work_tree_exists
  cd_to_toplevel
  
  LF='
@@@ -73,7 -40,7 +73,7 @@@ ok_to_skip_pre_rebase
  resolvemsg="
  When you have resolved this problem run \"git rebase --continue\".
  If you would prefer to skip this patch, instead run \"git rebase --skip\".
 -To restore the original branch and stop rebasing run \"git rebase --abort\".
 +To check out the original branch and stop rebasing run \"git rebase --abort\".
  "
  unset onto
  strategy=
@@@ -81,6 -48,7 +81,6 @@@ strategy_opts
  do_merge=
  merge_dir="$GIT_DIR"/rebase-merge
  apply_dir="$GIT_DIR"/rebase-apply
 -prec=4
  verbose=
  diffstat=
  test "$(git config --bool rebase.stat)" = true && diffstat=t
@@@ -98,53 -66,92 +98,53 @@@ state_dir
  action=
  preserve_merges=
  autosquash=
 +keep_empty=
  test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
  
 -read_state () {
 -      if test "$type" = merge
 -      then
 -              onto_name=$(cat "$state_dir"/onto_name) &&
 -              end=$(cat "$state_dir"/end) &&
 -              msgnum=$(cat "$state_dir"/msgnum)
 -      fi &&
 +read_basic_state () {
        head_name=$(cat "$state_dir"/head-name) &&
        onto=$(cat "$state_dir"/onto) &&
 -      orig_head=$(cat "$state_dir"/orig-head) &&
 -      GIT_QUIET=$(cat "$state_dir"/quiet)
 -}
 -
 -continue_merge () {
 -      test -d "$merge_dir" || die "$merge_dir directory does not exist"
 -
 -      unmerged=$(git ls-files -u)
 -      if test -n "$unmerged"
 +      # We always write to orig-head, but interactive rebase used to write to
 +      # head. Fall back to reading from head to cover for the case that the
 +      # user upgraded git with an ongoing interactive rebase.
 +      if test -f "$state_dir"/orig-head
        then
 -              echo "You still have unmerged paths in your index"
 -              echo "did you forget to use git add?"
 -              die "$resolvemsg"
 -      fi
 -
 -      cmt=`cat "$merge_dir/current"`
 -      if ! git diff-index --quiet --ignore-submodules HEAD --
 -      then
 -              if ! git commit --no-verify -C "$cmt"
 -              then
 -                      echo "Commit failed, please do not call \"git commit\""
 -                      echo "directly, but instead do one of the following: "
 -                      die "$resolvemsg"
 -              fi
 -              if test -z "$GIT_QUIET"
 -              then
 -                      printf "Committed: %0${prec}d " $msgnum
 -              fi
 -              echo "$cmt $(git rev-parse HEAD^0)" >> "$merge_dir/rewritten"
 +              orig_head=$(cat "$state_dir"/orig-head)
        else
 -              if test -z "$GIT_QUIET"
 -              then
 -                      printf "Already applied: %0${prec}d " $msgnum
 -              fi
 -      fi
 -      test -z "$GIT_QUIET" &&
 -      GIT_PAGER='' git log --format=%s -1 "$cmt"
 +              orig_head=$(cat "$state_dir"/head)
 +      fi &&
 +      GIT_QUIET=$(cat "$state_dir"/quiet) &&
 +      test -f "$state_dir"/verbose && verbose=t
 +      test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
 +      test -f "$state_dir"/strategy_opts &&
 +              strategy_opts="$(cat "$state_dir"/strategy_opts)"
 +      test -f "$state_dir"/allow_rerere_autoupdate &&
 +              allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
 +}
  
 -      # onto the next patch:
 -      msgnum=$(($msgnum + 1))
 -      echo "$msgnum" >"$merge_dir/msgnum"
 +write_basic_state () {
 +      echo "$head_name" > "$state_dir"/head-name &&
 +      echo "$onto" > "$state_dir"/onto &&
 +      echo "$orig_head" > "$state_dir"/orig-head &&
 +      echo "$GIT_QUIET" > "$state_dir"/quiet &&
 +      test t = "$verbose" && : > "$state_dir"/verbose
 +      test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
 +      test -n "$strategy_opts" && echo "$strategy_opts" > \
 +              "$state_dir"/strategy_opts
 +      test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
 +              "$state_dir"/allow_rerere_autoupdate
  }
  
 -call_merge () {
 -      cmt="$(cat "$merge_dir/cmt.$1")"
 -      echo "$cmt" > "$merge_dir/current"
 -      hd=$(git rev-parse --verify HEAD)
 -      cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
 -      msgnum=$(cat "$merge_dir/msgnum")
 -      eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
 -      eval GITHEAD_$hd='$onto_name'
 -      export GITHEAD_$cmt GITHEAD_$hd
 -      if test -n "$GIT_QUIET"
 -      then
 -              GIT_MERGE_VERBOSITY=1 && export GIT_MERGE_VERBOSITY
 -      fi
 -      test -z "$strategy" && strategy=recursive
 -      eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"'
 -      rv=$?
 -      case "$rv" in
 -      0)
 -              unset GITHEAD_$cmt GITHEAD_$hd
 -              return
 -              ;;
 -      1)
 -              git rerere $allow_rerere_autoupdate
 -              die "$resolvemsg"
 -              ;;
 -      2)
 -              echo "Strategy: $rv $strategy failed, try another" 1>&2
 -              die "$resolvemsg"
 +output () {
 +      case "$verbose" in
 +      '')
 +              output=$("$@" 2>&1 )
 +              status=$?
 +              test $status != 0 && printf "%s\n" "$output"
 +              return $status
                ;;
        *)
 -              die "Unknown exit code ($rv) from command:" \
 -                      "git-merge-$strategy $cmt^ -- HEAD $cmt"
 +              "$@"
                ;;
        esac
  }
@@@ -155,21 -162,29 +155,21 @@@ move_to_original_branch () 
                message="rebase finished: $head_name onto $onto"
                git update-ref -m "$message" \
                        $head_name $(git rev-parse HEAD) $orig_head &&
 -              git symbolic-ref HEAD $head_name ||
 +              git symbolic-ref \
 +                      -m "rebase finished: returning to $head_name" \
 +                      HEAD $head_name ||
                die "Could not move back to $head_name"
                ;;
        esac
  }
  
 -finish_rb_merge () {
 -      move_to_original_branch
 -      git notes copy --for-rewrite=rebase < "$merge_dir"/rewritten
 -      if test -x "$GIT_DIR"/hooks/post-rewrite &&
 -              test -s "$merge_dir"/rewritten; then
 -              "$GIT_DIR"/hooks/post-rewrite rebase < "$merge_dir"/rewritten
 -      fi
 -      rm -r "$merge_dir"
 -      say All done.
 -}
 -
 -run_interactive_rebase () {
 +run_specific_rebase () {
        if [ "$interactive_rebase" = implied ]; then
                GIT_EDITOR=:
                export GIT_EDITOR
 +              autosquash=
        fi
 -      . git-rebase--interactive "$@"
 +      . git-rebase--$type
  }
  
  run_pre_rebase_hook () {
@@@ -212,7 -227,7 +212,7 @@@ d
                ok_to_skip_pre_rebase=
                ;;
        --continue|--skip|--abort)
 -              test $total_argc -eq 1 || usage
 +              test $total_argc -eq 2 || usage
                action=${1##--}
                ;;
        --onto)
                onto="$2"
                shift
                ;;
 -      -i|--interactive)
 +      -i)
                interactive_rebase=explicit
                ;;
 -      -p|--preserve-merges)
 +      -k)
 +              keep_empty=yes
 +              ;;
 +      -p)
                preserve_merges=t
                test -z "$interactive_rebase" && interactive_rebase=implied
                ;;
        --no-autosquash)
                autosquash=
                ;;
 -      -M|-m|--m|--me|--mer|--merg|--merge)
 +      -M|-m)
                do_merge=t
                ;;
 -      -X*|--strategy-option*)
 -              case "$#,$1" in
 -              1,-X|1,--strategy-option)
 -                      usage ;;
 -              *,-X|*,--strategy-option)
 -                      newopt="$2"
 -                      shift ;;
 -              *,--strategy-option=*)
 -                      newopt="$(expr " $1" : ' --strategy-option=\(.*\)')" ;;
 -              *,-X*)
 -                      newopt="$(expr " $1" : ' -X\(.*\)')" ;;
 -              1,*)
 -                      usage ;;
 -              esac
 -              strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$newopt")"
 +      -X)
 +              shift
 +              strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$1")"
                do_merge=t
                test -z "$strategy" && strategy=recursive
                ;;
 -      -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
 -              --strateg=*|--strategy=*|\
 -      -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
 -              case "$#,$1" in
 -              *,*=*)
 -                      strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
 -              1,*)
 -                      usage ;;
 -              *)
 -                      strategy="$2"
 -                      shift ;;
 -              esac
 +      -s)
 +              shift
 +              strategy="$1"
                do_merge=t
                ;;
 -      -n|--no-stat)
 +      -n)
                diffstat=
                ;;
        --stat)
                diffstat=t
                ;;
 -      -v|--verbose)
 +      -v)
                verbose=t
                diffstat=t
                GIT_QUIET=
                ;;
 -      -q|--quiet)
 +      -q)
                GIT_QUIET=t
                git_am_opt="$git_am_opt -q"
                verbose=
                diffstat=
                ;;
 -      --whitespace=*)
 -              git_am_opt="$git_am_opt $1"
 +      --whitespace)
 +              shift
 +              git_am_opt="$git_am_opt --whitespace=$1"
                case "$1" in
 -              --whitespace=fix|--whitespace=strip)
 +              fix|strip)
                        force_rebase=t
                        ;;
                esac
                git_am_opt="$git_am_opt $1"
                force_rebase=t
                ;;
 -      -C*)
 -              git_am_opt="$git_am_opt $1"
 +      -C)
 +              shift
 +              git_am_opt="$git_am_opt -C$1"
                ;;
        --root)
                rebase_root=t
                ;;
 -      -f|--f|--fo|--for|--forc|--force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase|--no-ff)
 +      -f|--no-ff)
                force_rebase=t
                ;;
        --rerere-autoupdate|--no-rerere-autoupdate)
                allow_rerere_autoupdate="$1"
                ;;
 -      -*)
 -              usage
 -              ;;
 -      *)
 +      --)
 +              shift
                break
                ;;
        esac
@@@ -308,43 -341,62 +308,43 @@@ test $# -gt 2 && usag
  if test -n "$action"
  then
        test -z "$in_progress" && die "No rebase in progress?"
 -      test "$type" = interactive && run_interactive_rebase
 +      # Only interactive rebase uses detailed reflog messages
 +      if test "$type" = interactive && test "$GIT_REFLOG_ACTION" = rebase
 +      then
 +              GIT_REFLOG_ACTION="rebase -i ($action)"
 +              export GIT_REFLOG_ACTION
 +      fi
  fi
  
  case "$action" in
  continue)
 +      # Sanity check
 +      git rev-parse --verify HEAD >/dev/null ||
 +              die "Cannot read HEAD"
        git update-index --ignore-submodules --refresh &&
        git diff-files --quiet --ignore-submodules || {
                echo "You must edit all merge conflicts and then"
                echo "mark them as resolved using git add"
                exit 1
        }
 -      read_state
 -      if test -d "$merge_dir"
 -      then
 -              continue_merge
 -              while test "$msgnum" -le "$end"
 -              do
 -                      call_merge "$msgnum"
 -                      continue_merge
 -              done
 -              finish_rb_merge
 -              exit
 -      fi
 -      git am --resolved --3way --resolvemsg="$resolvemsg" &&
 -      move_to_original_branch
 -      exit
 +      read_basic_state
 +      run_specific_rebase
        ;;
  skip)
 -      git reset --hard HEAD || exit $?
 -      read_state
 -      if test -d "$merge_dir"
 -      then
 -              git rerere clear
 -              msgnum=$(($msgnum + 1))
 -              while test "$msgnum" -le "$end"
 -              do
 -                      call_merge "$msgnum"
 -                      continue_merge
 -              done
 -              finish_rb_merge
 -              exit
 -      fi
 -      git am -3 --skip --resolvemsg="$resolvemsg" &&
 -      move_to_original_branch
 -      exit
 +      output git reset --hard HEAD || exit $?
 +      read_basic_state
 +      run_specific_rebase
        ;;
  abort)
        git rerere clear
 -      read_state
 +      read_basic_state
        case "$head_name" in
        refs/*)
 -              git symbolic-ref HEAD $head_name ||
 +              git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
                die "Could not move back to $head_name"
                ;;
        esac
 -      git reset --hard $orig_head
 +      output git reset --hard $orig_head
        rm -r "$state_dir"
        exit
        ;;
@@@ -364,6 -416,8 +364,6 @@@ and run me again.  I am stopping in cas
  valuable there.'
  fi
  
 -test $# -eq 0 && test -z "$rebase_root" && usage
 -
  if test -n "$interactive_rebase"
  then
        type=interactive
  
  if test -z "$rebase_root"
  then
 -      # The upstream head must be given.  Make sure it is valid.
 -      upstream_name="$1"
 -      shift
 +      case "$#" in
 +      0)
 +              if ! upstream_name=$(git rev-parse --symbolic-full-name \
 +                      --verify -q @{upstream} 2>/dev/null)
 +              then
 +                      . git-parse-remote
 +                      error_on_missing_default_upstream "rebase" "rebase" \
 +                              "against" "git rebase <branch>"
 +              fi
 +              ;;
 +      *)      upstream_name="$1"
 +              shift
 +              ;;
 +      esac
        upstream=`git rev-parse --verify "${upstream_name}^0"` ||
        die "invalid upstream $upstream_name"
 -      unset root_flag
        upstream_arg="$upstream_name"
  else
        test -z "$onto" && die "You must specify --onto when using --root"
        unset upstream_name
        unset upstream
 -      root_flag="--root"
 -      upstream_arg="$root_flag"
 +      upstream_arg=--root
  fi
  
  # Make sure the branch to rebase onto is valid.
@@@ -424,7 -469,7 +424,7 @@@ case "$onto_name" i
        ;;
  *)
        onto=$(git rev-parse --verify "${onto_name}^0") ||
-       die "Does not point to a valid commit: $1"
+       die "Does not point to a valid commit: $onto_name"
        ;;
  esac
  
@@@ -440,14 -485,15 +440,14 @@@ case "$#" i
        switch_to="$1"
  
        if git show-ref --verify --quiet -- "refs/heads/$1" &&
 -         branch=$(git rev-parse -q --verify "refs/heads/$1")
 +         orig_head=$(git rev-parse -q --verify "refs/heads/$1")
        then
                head_name="refs/heads/$1"
 -      elif branch=$(git rev-parse -q --verify "$1")
 +      elif orig_head=$(git rev-parse -q --verify "$1")
        then
                head_name="detached HEAD"
        else
 -              echo >&2 "fatal: no such branch: $1"
 -              usage
 +              die "fatal: no such branch: $1"
        fi
        ;;
  *)
                head_name="detached HEAD"
                branch_name=HEAD ;# detached
        fi
 -      branch=$(git rev-parse --verify "${branch_name}^0") || exit
 +      orig_head=$(git rev-parse --verify "${branch_name}^0") || exit
        ;;
  esac
 -orig_head=$branch
 -
 -test "$type" = interactive && run_interactive_rebase "$@"
  
  require_clean_work_tree "rebase" "Please commit or stash them."
  
 -# Now we are rebasing commits $upstream..$branch (or with --root,
 -# everything leading up to $branch) on top of $onto
 +# Now we are rebasing commits $upstream..$orig_head (or with --root,
 +# everything leading up to $orig_head) on top of $onto
  
  # Check if we are already based on $onto with linear history,
 -# but this should be done only when upstream and onto are the same.
 -mb=$(git merge-base "$onto" "$branch")
 -if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
 +# but this should be done only when upstream and onto are the same
 +# and if this is not an interactive rebase.
 +mb=$(git merge-base "$onto" "$orig_head")
 +if test "$type" != interactive && test "$upstream" = "$onto" &&
 +      test "$mb" = "$onto" &&
        # linear history?
 -      ! (git rev-list --parents "$onto".."$branch" | sane_grep " .* ") > /dev/null
 +      ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
  then
        if test -z "$force_rebase"
        then
@@@ -492,6 -539,11 +492,6 @@@ f
  # If a hook exists, give it a chance to interrupt
  run_pre_rebase_hook "$upstream_arg" "$@"
  
 -# Detach HEAD and reset the tree
 -say "First, rewinding head to replay your work on top of it..."
 -git checkout -q "$onto^0" || die "could not detach HEAD"
 -git update-ref ORIG_HEAD $branch
 -
  if test -n "$diffstat"
  then
        if test -n "$verbose"
        GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
  fi
  
 +test "$type" = interactive && run_specific_rebase
 +
 +# Detach HEAD and reset the tree
 +say "First, rewinding head to replay your work on top of it..."
 +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
  # we just fast-forwarded.
 -if test "$mb" = "$branch"
 +if test "$mb" = "$orig_head"
  then
        say "Fast-forwarded $branch_name to $onto_name."
        move_to_original_branch
@@@ -525,4 -570,49 +525,4 @@@ els
        revisions="$upstream..$orig_head"
  fi
  
 -if test -z "$do_merge"
 -then
 -      git format-patch -k --stdout --full-index --ignore-if-in-upstream \
 -              --src-prefix=a/ --dst-prefix=b/ \
 -              --no-renames $root_flag "$revisions" |
 -      git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" &&
 -      move_to_original_branch
 -      ret=$?
 -      test 0 != $ret -a -d "$apply_dir" &&
 -              echo $head_name > "$apply_dir/head-name" &&
 -              echo $onto > "$apply_dir/onto" &&
 -              echo $orig_head > "$apply_dir/orig-head" &&
 -              echo "$GIT_QUIET" > "$apply_dir/quiet"
 -      exit $ret
 -fi
 -
 -# start doing a rebase with git-merge
 -# this is rename-aware if the recursive (default) strategy is used
 -
 -mkdir -p "$merge_dir"
 -echo "$onto_name" > "$merge_dir/onto_name"
 -echo "$head_name" > "$merge_dir/head-name"
 -echo "$onto" > "$merge_dir/onto"
 -echo "$orig_head" > "$merge_dir/orig-head"
 -echo "$GIT_QUIET" > "$merge_dir/quiet"
 -
 -msgnum=0
 -for cmt in `git rev-list --reverse --no-merges "$revisions"`
 -do
 -      msgnum=$(($msgnum + 1))
 -      echo "$cmt" > "$merge_dir/cmt.$msgnum"
 -done
 -
 -echo 1 >"$merge_dir/msgnum"
 -echo $msgnum >"$merge_dir/end"
 -
 -end=$msgnum
 -msgnum=1
 -
 -while test "$msgnum" -le "$end"
 -do
 -      call_merge "$msgnum"
 -      continue_merge
 -done
 -
 -finish_rb_merge
 +run_specific_rebase