Merge branch 'js/rebase-i-clean-up-upon-continue-to-skip'
authorJunio C Hamano <gitster@pobox.com>
Mon, 13 Jul 2015 21:00:25 +0000 (14:00 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 13 Jul 2015 21:00:25 +0000 (14:00 -0700)
Abandoning an already applied change in "git rebase -i" with
"--continue" left CHERRY_PICK_HEAD and confused later steps.

* js/rebase-i-clean-up-upon-continue-to-skip:
rebase -i: do not leave a CHERRY_PICK_HEAD file behind
t3404: demonstrate CHERRY_PICK_HEAD bug

1  2 
git-rebase--interactive.sh
t/t3404-rebase-interactive.sh
index dc3133f681227e66dd54f7bed4be68277e4240b3,3b361229b75317359c90f854cf19e3853349e382..5ff0f1c81ac8e72ce6a2358ca2fced0650571030
@@@ -132,16 -132,6 +132,16 @@@ mark_action_done () 
        fi
  }
  
 +# Put the last action marked done at the beginning of the todo list
 +# again. If there has not been an action marked done yet, leave the list of
 +# items on the todo list unchanged.
 +reschedule_last_action () {
 +      tail -n 1 "$done" | cat - "$todo" >"$todo".new
 +      sed -e \$d <"$done" >"$done".new
 +      mv -f "$todo".new "$todo"
 +      mv -f "$done".new "$done"
 +}
 +
  append_todo_help () {
        git stripspace --comment-lines >>"$todo" <<\EOF
  
@@@ -262,12 -252,6 +262,12 @@@ pick_one () 
        output eval git cherry-pick \
                        ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
                        "$strategy_args" $empty_args $ff "$@"
 +
 +      # If cherry-pick dies it leaves the to-be-picked commit unrecorded. Reschedule
 +      # previous task so this commit is not lost.
 +      ret=$?
 +      case "$ret" in [01]) ;; *) reschedule_last_action ;; esac
 +      return $ret
  }
  
  pick_one_preserving_merges () {
@@@ -502,7 -486,7 +502,7 @@@ do_pick () 
  }
  
  do_next () {
 -      rm -f "$msg" "$author_script" "$amend" || exit
 +      rm -f "$msg" "$author_script" "$amend" "$state_dir"/stopped-sha || exit
        read -r command sha1 rest < "$todo"
        case "$command" in
        "$comment_char"*|''|noop)
                read -r command rest < "$todo"
                mark_action_done
                printf 'Executing: %s\n' "$rest"
 -              # "exec" command doesn't take a sha1 in the todo-list.
 -              # => can't just use $sha1 here.
 -              git rev-parse --verify HEAD > "$state_dir"/stopped-sha
                ${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution
                status=$?
                # Run in subshell because require_clean_work_tree can die.
                git notes copy --for-rewrite=rebase < "$rewritten_list" ||
                true # we don't care if this copying failed
        } &&
 -      if test -x "$GIT_DIR"/hooks/post-rewrite &&
 -              test -s "$rewritten_list"; then
 -              "$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list"
 +      hook="$(git rev-parse --git-path hooks/post-rewrite)"
 +      if test -x "$hook" && test -s "$rewritten_list"; then
 +              "$hook" rebase < "$rewritten_list"
                true # we don't care if this hook failed
        fi &&
        warn "Successfully rebased and updated $head_name."
@@@ -849,7 -836,11 +849,11 @@@ continue
        # do we have anything to commit?
        if git diff-index --cached --quiet HEAD --
        then
-               : Nothing to commit -- skip this
+               # Nothing to commit -- skip this commit
+               test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD ||
+               rm "$GIT_DIR"/CHERRY_PICK_HEAD ||
+               die "Could not remove CHERRY_PICK_HEAD"
        else
                if ! test -f "$author_script"
                then
@@@ -887,10 -878,7 +891,10 @@@ first and then run 'git rebase --contin
                fi
        fi
  
 -      record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
 +      if test -r "$state_dir"/stopped-sha
 +      then
 +              record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
 +      fi
  
        require_clean_work_tree "rebase"
        do_rest
@@@ -977,13 -965,14 +981,13 @@@ els
        revisions=$onto...$orig_head
        shortrevisions=$shorthead
  fi
 -git rev-list $merges_option --pretty=oneline --abbrev-commit \
 -      --abbrev=7 --reverse --left-right --topo-order \
 +git rev-list $merges_option --pretty=oneline --reverse --left-right --topo-order \
        $revisions ${restrict_revision+^$restrict_revision} | \
        sed -n "s/^>//p" |
 -while read -r shortsha1 rest
 +while read -r sha1 rest
  do
  
 -      if test -z "$keep_empty" && is_empty_commit $shortsha1 && ! is_merge_commit $shortsha1
 +      if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
        then
                comment_out="$comment_char "
        else
  
        if test t != "$preserve_merges"
        then
 -              printf '%s\n' "${comment_out}pick $shortsha1 $rest" >>"$todo"
 +              printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
        else
 -              sha1=$(git rev-parse $shortsha1)
                if test -z "$rebase_root"
                then
                        preserve=t
                if test f = "$preserve"
                then
                        touch "$rewritten"/$sha1
 -                      printf '%s\n' "${comment_out}pick $shortsha1 $rest" >>"$todo"
 +                      printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
                fi
        fi
  done
@@@ -1034,8 -1024,8 +1038,8 @@@ the
                        # 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)
 -                      sane_grep -v "^[a-z][a-z]* $short" <"$todo" > "${todo}2" ; mv "${todo}2" "$todo"
 +                      sha1=$(git rev-list -1 $rev)
 +                      sane_grep -v "^[a-z][a-z]* $sha1" <"$todo" > "${todo}2" ; mv "${todo}2" "$todo"
                        rm "$rewritten"/$rev
                fi
        done
@@@ -1045,12 -1035,9 +1049,12 @@@ test -s "$todo" || echo noop >> "$todo
  test -n "$autosquash" && rearrange_squash "$todo"
  test -n "$cmd" && add_exec_commands "$todo"
  
 +todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
 +todocount=${todocount##* }
 +
  cat >>"$todo" <<EOF
  
 -$comment_char Rebase $shortrevisions onto $shortonto
 +$comment_char Rebase $shortrevisions onto $shortonto ($todocount command(s))
  EOF
  append_todo_help
  git stripspace --comment-lines >>"$todo" <<\EOF
@@@ -1069,7 -1056,6 +1073,7 @@@ has_action "$todo" |
        return 2
  
  cp "$todo" "$todo".backup
 +collapse_todo_ids
  git_sequence_editor "$todo" ||
        die_abort "Could not execute editor"
  
index ac429a0bbbbeb95f96336aa203a5d9726ed6eb1d,914020176dd00baac3229aa285733a14b9e20470..467e6c1ed526d4bfffbab2e1479e1334058506de
@@@ -950,7 -950,7 +950,7 @@@ test_expect_success 'rebase --edit-tod
        set_fake_editor &&
        FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 &&
        FAKE_LINES="2 1" git rebase --edit-todo &&
 -      git rebase --continue
 +      git rebase --continue &&
        test M = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test L = $(git cat-file commit HEAD | sed -ne \$p)
  '
@@@ -1007,7 -1007,7 +1007,7 @@@ test_expect_success 'rebase -i with --s
  '
  
  test_expect_success 'rebase -i error on commits with \ in message' '
 -      current_head=$(git rev-parse HEAD)
 +      current_head=$(git rev-parse HEAD) &&
        test_when_finished "git rebase --abort; git reset --hard $current_head; rm -f error" &&
        test_commit TO-REMOVE will-conflict old-content &&
        test_commit "\temp" will-conflict new-content dummy &&
@@@ -1039,67 -1039,25 +1039,88 @@@ test_expect_success 'short SHA-1 collid
        )
  '
  
 +test_expect_success 'respect core.abbrev' '
 +      git config core.abbrev 12 &&
 +      set_cat_todo_editor &&
 +      test_must_fail git rebase -i HEAD~4 >todo-list &&
 +      test 4 = $(grep -c "pick [0-9a-f]\{12,\}" todo-list)
 +'
 +
 +test_expect_success 'todo count' '
 +      write_script dump-raw.sh <<-\EOF &&
 +              cat "$1"
 +      EOF
 +      test_set_editor "$(pwd)/dump-raw.sh" &&
 +      git rebase -i HEAD~4 >actual &&
 +      grep "^# Rebase ..* onto ..* ([0-9]" actual
 +'
 +
 +test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
 +      git checkout --force branch2 &&
 +      git clean -f &&
 +      set_fake_editor &&
 +      FAKE_LINES="edit 1 2" git rebase -i A &&
 +      test_cmp_rev HEAD F &&
 +      test_path_is_missing file6 &&
 +      >file6 &&
 +      test_must_fail git rebase --continue &&
 +      test_cmp_rev HEAD F &&
 +      rm file6 &&
 +      git rebase --continue &&
 +      test_cmp_rev HEAD I
 +'
 +
 +test_expect_success 'rebase -i commits that overwrite untracked files (squash)' '
 +      git checkout --force branch2 &&
 +      git clean -f &&
 +      git tag original-branch2 &&
 +      set_fake_editor &&
 +      FAKE_LINES="edit 1 squash 2" git rebase -i A &&
 +      test_cmp_rev HEAD F &&
 +      test_path_is_missing file6 &&
 +      >file6 &&
 +      test_must_fail git rebase --continue &&
 +      test_cmp_rev HEAD F &&
 +      rm file6 &&
 +      git rebase --continue &&
 +      test $(git cat-file commit HEAD | sed -ne \$p) = I &&
 +      git reset --hard original-branch2
 +'
 +
 +test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' '
 +      git checkout --force branch2 &&
 +      git clean -f &&
 +      set_fake_editor &&
 +      FAKE_LINES="edit 1 2" git rebase -i --no-ff A &&
 +      test $(git cat-file commit HEAD | sed -ne \$p) = F &&
 +      test_path_is_missing file6 &&
 +      >file6 &&
 +      test_must_fail git rebase --continue &&
 +      test $(git cat-file commit HEAD | sed -ne \$p) = F &&
 +      rm file6 &&
 +      git rebase --continue &&
 +      test $(git cat-file commit HEAD | sed -ne \$p) = I
 +'
 +
+ test_expect_success 'rebase --continue removes CHERRY_PICK_HEAD' '
+       git checkout -b commit-to-skip &&
+       for double in X 3 1
+       do
+               test_seq 5 | sed "s/$double/&&/" >seq &&
+               git add seq &&
+               test_tick &&
+               git commit -m seq-$double
+       done &&
+       git tag seq-onto &&
+       git reset --hard HEAD~2 &&
+       git cherry-pick seq-onto &&
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES= git rebase -i seq-onto &&
+       test -d .git/rebase-merge &&
+       git rebase --continue &&
+       git diff --exit-code seq-onto &&
+       test ! -d .git/rebase-merge &&
+       test ! -f .git/CHERRY_PICK_HEAD
+ '
  test_done