X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=git-rebase--interactive.sh;h=9187e9bdba4792ab9d0f5532128ff87e9329ab1e;hb=812d2a3d61b1bbb1931aff2ed6d2a17e939f5bf2;hp=1569a7ad915988cfc14be71eb550debcd134f864;hpb=a25eb13909088f04f87acec26c522cc555f6b4a9;p=git.git diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 1569a7ad9..9187e9bdb 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -28,6 +28,7 @@ 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) +autosquash move commits that begin with squash!/fixup! under -i " . git-sh-setup @@ -101,6 +102,7 @@ ONTO= VERBOSE= OK_TO_SKIP_PRE_REBASE= REBASE_ROOT= +AUTOSQUASH= GIT_CHERRY_PICK_HELP=" After resolving the conflicts, mark the corrected paths with 'git add ', and @@ -213,10 +215,10 @@ has_action () { # Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and # GIT_AUTHOR_DATE exported from the current environment. do_with_author () { - GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ - GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ - GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ - "$@" + ( + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE + "$@" + ) } pick_one () { @@ -233,7 +235,7 @@ pick_one () { parent_sha1=$(git rev-parse --verify $sha1^) || die "Could not get the parent of $sha1" current_sha1=$(git rev-parse --verify HEAD) - if test -z "$no_ff" -a "$current_sha1" = "$parent_sha1" + if test -z "$no_ff" && test "$current_sha1" = "$parent_sha1" then output git reset --hard $sha1 output warn Fast-forward to $(git rev-parse --short $sha1) @@ -408,6 +410,21 @@ peek_next_command () { sed -n -e "/^#/d" -e "/^$/d" -e "s/ .*//p" -e "q" < "$TODO" } +# A squash/fixup has failed. Prepare the long version of the squash +# commit message, then die_with_patch. This code path requires the +# user to edit the combined commit message for all commits that have +# been squashed/fixedup so far. So also erase the old squash +# messages, effectively causing the combined commit to be used as the +# new basis for any further squash/fixups. Args: sha1 rest +die_failed_squash() { + mv "$SQUASH_MSG" "$MSG" || exit + rm -f "$FIXUP_MSG" + cp "$MSG" "$GIT_DIR"/MERGE_MSG || exit + warn + warn "Could not apply $1... $2" + die_with_patch $1 "" +} + do_next () { rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit read command sha1 rest < "$TODO" @@ -465,58 +482,31 @@ do_next () { mark_action_done update_squash_messages $squash_style $sha1 - failed=f author_script=$(get_author_ident_from_commit HEAD) echo "$author_script" > "$AUTHOR_SCRIPT" eval "$author_script" output git reset --soft HEAD^ - pick_one -n $sha1 || failed=t + pick_one -n $sha1 || die_failed_squash $sha1 "$rest" 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: - if test $failed = f - then - do_with_author output git commit --no-verify -F "$SQUASH_MSG" || - failed=t - fi - if test $failed = t - then - cp "$SQUASH_MSG" "$MSG" || exit - # After any kind of hiccup, prevent committing without - # opening the commit message editor: - rm -f "$FIXUP_MSG" - cp "$MSG" "$GIT_DIR"/MERGE_MSG || exit - warn - warn "Could not apply $sha1... $rest" - die_with_patch $sha1 "" - fi + do_with_author output git commit --no-verify -F "$SQUASH_MSG" || + die_failed_squash $sha1 "$rest" ;; *) # This is the final command of this squash/fixup group - if test $failed = f + if test -f "$FIXUP_MSG" then - if test -f "$FIXUP_MSG" - then - do_with_author git commit --no-verify -F "$FIXUP_MSG" || - failed=t - else - cp "$SQUASH_MSG" "$GIT_DIR"/SQUASH_MSG || exit - rm -f "$GIT_DIR"/MERGE_MSG - do_with_author git commit --no-verify -e || - failed=t - fi - fi - rm -f "$FIXUP_MSG" - if test $failed = t - then - mv "$SQUASH_MSG" "$MSG" || exit - cp "$MSG" "$GIT_DIR"/MERGE_MSG || exit - warn - warn "Could not apply $sha1... $rest" - die_with_patch $sha1 "" + do_with_author git commit --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 || + die_failed_squash $sha1 "$rest" fi - rm -f "$SQUASH_MSG" + rm -f "$SQUASH_MSG" "$FIXUP_MSG" ;; esac ;; @@ -601,6 +591,56 @@ get_saved_options () { test -f "$DOTEST"/rebase-root && REBASE_ROOT=t } +# 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 +# "fixup"/"squash". +rearrange_squash () { + sed -n -e 's/^pick \([0-9a-f]*\) \(squash\)! /\1 \2 /p' \ + -e 's/^pick \([0-9a-f]*\) \(fixup\)! /\1 \2 /p' \ + "$1" >"$1.sq" + test -s "$1.sq" || return + + used= + while read pick sha1 message + do + case " $used" in + *" $sha1 "*) continue ;; + esac + echo "$pick $sha1 $message" + while read squash action msg + do + case "$message" in + "$msg"*) + echo "$action $squash $action! $msg" + used="$used$squash " + ;; + esac + done <"$1.sq" + done >"$1.rearranged" <"$1" + cat "$1.rearranged" >"$1" + rm -f "$1.sq" "$1.rearranged" +} + +LF=' +' +parse_onto () { + case "$1" in + *...*) + if left=${1%...*} right=${1#*...} && + onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD}) + then + case "$onto" in + ?*"$LF"?* | '') + exit 1 ;; + esac + echo "$onto" + exit 0 + fi + esac + git rev-parse --verify "$1^0" +} + while test $# != 0 do case "$1" in @@ -705,9 +745,12 @@ first and then run 'git rebase --continue' again." --root) REBASE_ROOT=t ;; + --autosquash) + AUTOSQUASH=t + ;; --onto) shift - ONTO=$(git rev-parse --verify "$1") || + ONTO=$(parse_onto "$1") || die "Does not point to a valid commit: $1" ;; --) @@ -857,6 +900,7 @@ first and then run 'git rebase --continue' again." fi test -s "$TODO" || echo noop >> "$TODO" + test -n "$AUTOSQUASH" && rearrange_squash "$TODO" cat >> "$TODO" << EOF # Rebase $SHORTREVISIONS onto $SHORTONTO @@ -878,7 +922,7 @@ EOF cp "$TODO" "$TODO".backup git_editor "$TODO" || - die "Could not execute editor" + die_abort "Could not execute editor" has_action "$TODO" || die_abort "Nothing to do"