From: Junio C Hamano Date: Tue, 29 Jan 2013 19:18:31 +0000 (-0800) Subject: Merge branch 'ph/rebase-preserve-all-merges' into maint X-Git-Tag: v1.8.1.3~20 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/da2987d4c3be50bb9d888d145ba42fe26c7045f3?ds=inline;hp=-c Merge branch 'ph/rebase-preserve-all-merges' into maint "git rebase --preserve-merges" lost empty merges in recent versions of Git. * ph/rebase-preserve-all-merges: rebase --preserve-merges: keep all merge commits including empty ones --- da2987d4c3be50bb9d888d145ba42fe26c7045f3 diff --combined git-rebase--interactive.sh index 44901d53c4,2fed92fac1..8ed7fccc18 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@@ -9,7 -9,9 +9,7 @@@ # # The original idea comes from Eric W. Biederman, in # http://article.gmane.org/gmane.comp.version-control.git/22407 - -. git-sh-setup - +# # The file containing rebase commands, comments, and empty lines. # This file is created by "git rebase -i" then edited by the user. As # the lines are processed, they are removed from the front of this @@@ -115,23 -117,6 +115,23 @@@ mark_action_done () fi } +append_todo_help () { + cat >> "$todo" << EOF +# +# Commands: +# p, pick = use commit +# r, reword = use commit, but edit the commit message +# e, edit = use commit, but stop for amending +# s, squash = use commit, but meld into previous commit +# f, fixup = like "squash", but discard this commit's log message +# x, exec = run command (the rest of the line) using shell +# +# These lines can be re-ordered; they are executed from top to bottom. +# +# If you remove a line here THAT COMMIT WILL BE LOST. +EOF +} + make_patch () { sha1_and_parents="$(git rev-list --parents -1 "$1")" case "$sha1_and_parents" in @@@ -190,6 -175,11 +190,11 @@@ is_empty_commit() test "$tree" = "$ptree" } + is_merge_commit() + { + git rev-parse --verify --quiet "$1"^2 >/dev/null 2>&1 + } + # Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and # GIT_AUTHOR_DATE exported from the current environment. do_with_author () { @@@ -432,29 -422,6 +437,29 @@@ record_in_rewritten() esac } +do_pick () { + if test "$(git rev-parse HEAD)" = "$squash_onto" + then + # Set the correct commit message and author info on the + # sentinel root before cherry-picking the original changes + # without committing (-n). Finally, update the sentinel again + # to include these changes. If the cherry-pick results in a + # conflict, this means our behaviour is similar to a standard + # failed cherry-pick during rebase, with a dirty index to + # resolve before manually running git commit --amend then git + # rebase --continue. + git commit --allow-empty --allow-empty-message --amend \ + --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 || + die_with_patch $1 "Could not apply $1... $2" + else + pick_one $1 || + die_with_patch $1 "Could not apply $1... $2" + fi +} + do_next () { rm -f "$msg" "$author_script" "$amend" || exit read -r command sha1 rest < "$todo" @@@ -466,14 -433,16 +471,14 @@@ comment_for_reflog pick mark_action_done - pick_one $sha1 || - die_with_patch $sha1 "Could not apply $sha1... $rest" + do_pick $sha1 "$rest" record_in_rewritten $sha1 ;; reword|r) comment_for_reflog reword mark_action_done - pick_one $sha1 || - die_with_patch $sha1 "Could not apply $sha1... $rest" + do_pick $sha1 "$rest" git commit --amend --no-post-rewrite || { 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" @@@ -487,7 -456,8 +492,7 @@@ comment_for_reflog edit mark_action_done - pick_one $sha1 || - die_with_patch $sha1 "Could not apply $sha1... $rest" + do_pick $sha1 "$rest" warn "Stopped at $sha1... $rest" exit_with_patch $sha1 0 ;; @@@ -510,28 -480,25 +515,28 @@@ author_script_content=$(get_author_ident_from_commit HEAD) echo "$author_script_content" > "$author_script" eval "$author_script_content" - output git reset --soft HEAD^ - pick_one -n $sha1 || die_failed_squash $sha1 "$rest" + if ! pick_one -n $sha1 + then + git rev-parse --verify HEAD >"$amend" + die_failed_squash $sha1 "$rest" + fi case "$(peek_next_command)" in 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 --no-verify -F "$squash_msg" || + do_with_author output git commit --amend --no-verify -F "$squash_msg" || 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 --no-verify -F "$fixup_msg" || + do_with_author git commit --amend --no-verify -F "$fixup_msg" || 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 --no-verify -e || + do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e || die_failed_squash $sha1 "$rest" fi rm -f "$squash_msg" "$fixup_msg" @@@ -561,10 -528,6 +566,10 @@@ warn warn " git rebase --continue" warn + if test $status -eq 127 # command not found + then + status=1 + fi exit "$status" elif test "$dirty" = t then @@@ -579,22 -542,22 +584,22 @@@ ;; *) warn "Unknown command: $command $sha1 $rest" + fixtodo="Please fix this using 'git rebase --edit-todo'." if git rev-parse --verify -q "$sha1" >/dev/null then - die_with_patch $sha1 "Please fix this in the file $todo." + die_with_patch $sha1 "$fixtodo" else - die "Please fix this in the file $todo." + die "$fixtodo" fi ;; esac test -s "$todo" && return comment_for_reflog finish && - shortonto=$(git rev-parse --short $onto) && newhead=$(git rev-parse HEAD) && case $head_name in refs/*) - message="$GIT_REFLOG_ACTION: $head_name onto $shortonto" && + message="$GIT_REFLOG_ACTION: $head_name onto $onto" && git update-ref -m "$message" $head_name $newhead $orig_head && git symbolic-ref \ -m "$GIT_REFLOG_ACTION: returning to $head_name" \ @@@ -726,27 -689,6 +731,27 @@@ rearrange_squash () rm -f "$1.sq" "$1.rearranged" } +# Add commands after a pick or after a squash/fixup serie +# in the todo list. +add_exec_commands () { + { + first=t + while read -r insn rest + do + case $insn in + pick) + test -n "$first" || + printf "%s" "$cmd" + ;; + esac + printf "%s %s\n" "$insn" "$rest" + first= + done + printf "%s" "$cmd" + } <"$1" >"$1.new" && + mv "$1.new" "$1" +} + case "$action" in continue) # do we have anything to commit? @@@ -772,6 -714,7 +777,6 @@@ In both case, once you're done, continu fi . "$author_script" || die "Error trying to find the author identity to amend commit" - current_head= if test -f "$amend" then current_head=$(git rev-parse --verify HEAD) @@@ -779,12 -722,13 +784,12 @@@ die "\ You have uncommitted changes in your working tree. Please, commit them first and then run 'git rebase --continue' again." - git reset --soft HEAD^ || - die "Cannot rewind the HEAD" + do_with_author git commit --amend --no-verify -F "$msg" -e || + die "Could not commit staged changes." + else + do_with_author git commit --no-verify -F "$msg" -e || + die "Could not commit staged changes." fi - do_with_author git commit --no-verify -F "$msg" -e || { - test -n "$current_head" && git reset --soft $current_head - die "Could not commit staged changes." - } fi record_in_rewritten "$(cat "$state_dir"/stopped-sha)" @@@ -797,23 -741,6 +802,23 @@@ skip do_rest ;; +edit-todo) + sed -e '/^#/d' < "$todo" > "$todo".new + mv -f "$todo".new "$todo" + append_todo_help + cat >> "$todo" << EOF +# +# You are editing the todo file of an ongoing interactive rebase. +# To continue rebase after editing, run: +# git rebase --continue +# +EOF + + git_sequence_editor "$todo" || + die "Could not execute editor" + + exit + ;; esac git var GIT_COMMITTER_IDENT >/dev/null || @@@ -874,7 -801,7 +879,7 @@@ git rev-list $merges_option --pretty=on while read -r shortsha1 rest do - if test -z "$keep_empty" && is_empty_commit $shortsha1 + if test -z "$keep_empty" && is_empty_commit $shortsha1 && ! is_merge_commit $shortsha1 then comment_out="# " else @@@ -935,15 -862,21 +940,15 @@@ f test -s "$todo" || echo noop >> "$todo" test -n "$autosquash" && rearrange_squash "$todo" +test -n "$cmd" && add_exec_commands "$todo" + cat >> "$todo" << EOF # Rebase $shortrevisions onto $shortonto +EOF +append_todo_help +cat >> "$todo" << EOF # -# Commands: -# p, pick = use commit -# r, reword = use commit, but edit the commit message -# e, edit = use commit, but stop for amending -# s, squash = use commit, but meld into previous commit -# f, fixup = like "squash", but discard this commit's log message -# x, exec = run command (the rest of the line) using shell -# -# These lines can be re-ordered; they are executed from top to bottom. -# -# If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. # EOF