From: Junio C Hamano Date: Mon, 16 Jun 2014 17:06:57 +0000 (-0700) Subject: Merge branch 'rr/rebase-autostash-fix' X-Git-Tag: v2.1.0-rc0~128 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/4a43d4f98a1a6a472ce1e87c64cb6df0d1653d02?hp=-c Merge branch 'rr/rebase-autostash-fix' * rr/rebase-autostash-fix: rebase -i: test "Nothing to do" case with autostash rebase -i: handle "Nothing to do" case with autostash --- 4a43d4f98a1a6a472ce1e87c64cb6df0d1653d02 diff --combined git-rebase--interactive.sh index 6ec9d3cb40,85f0e93eee..f267d8b6c3 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@@ -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 () { @@@ -352,10 -340,9 +352,10 @@@ 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" @@@ -363,9 -350,7 +363,9 @@@ 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" @@@ -549,22 -533,19 +549,22 @@@ 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 ;; *) @@@ -697,32 -678,6 +697,32 @@@ 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 @@@ -758,7 -699,7 +758,7 @@@ 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 @@@ -773,7 -714,7 +773,7 @@@ 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 ;; @@@ -789,8 -730,7 +789,8 @@@ 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? @@@ -840,15 -769,14 +840,15 @@@ 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: @@@ -864,12 -792,10 +864,12 @@@ 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?" @@@ -1049,24 -970,17 +1049,24 @@@ f 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 5c7a0a1a58,9eb276ba20..06c810b64f --- a/git-rebase.sh +++ b/git-rebase.sh @@@ -5,7 -5,6 +5,7 @@@ SUBDIRECTORY_OK=Yes OPTIONS_KEEPDASHDASH= +OPTIONS_STUCKLONG=t OPTIONS_SPEC="\ git rebase [-i] [options] [--exec ] [--onto ] [] [] git rebase [-i] [options] [--exec ] [--onto ] --root [] @@@ -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" && @@@ -109,8 -104,6 +109,8 @@@ 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 () { @@@ -124,7 -117,6 +124,7 @@@ "$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") @@@ -163,14 -155,21 +163,18 @@@ 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 } @@@ -233,19 -237,23 +242,19 @@@ d 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 ;; @@@ -255,25 -263,21 +264,25 @@@ --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) @@@ -282,20 -286,21 +291,20 @@@ --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 ;; @@@ -308,24 -313,19 +317,24 @@@ 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 " 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 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