From: Junio C Hamano Date: Sat, 3 Apr 2010 19:28:44 +0000 (-0700) Subject: Merge branch 'mb/rebase-i-no-ff' X-Git-Tag: v1.7.1-rc0~9 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/9234b003726556e0d3d0ea8ade97dc9f7bd50a6e?ds=inline;hp=-c Merge branch 'mb/rebase-i-no-ff' * mb/rebase-i-no-ff: Teach rebase the --no-ff option. Conflicts: git-rebase--interactive.sh t/t3404-rebase-interactive.sh --- 9234b003726556e0d3d0ea8ade97dc9f7bd50a6e diff --combined git-rebase--interactive.sh index 2ff211cbaa,d5468b0478..b817c4a76e --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@@ -20,6 -20,7 +20,7 @@@ v,verbose display a diffstat o 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 always used (no-op) i,interactive always used (no-op) Actions: @@@ -96,13 -97,6 +97,13 @@@ AUTHOR_SCRIPT="$DOTEST"/author-scrip # command is processed, this file is deleted. AMEND="$DOTEST"/amend +# For the post-rewrite hook, we make a list of rewritten commits and +# their new sha1s. The rewritten-pending list keeps the sha1s of +# commits that have been processed, but not committed yet, +# e.g. because they are waiting for a 'squash' command. +REWRITTEN_LIST="$DOTEST"/rewritten-list +REWRITTEN_PENDING="$DOTEST"/rewritten-pending + PRESERVE_MERGES= STRATEGY= ONTO= @@@ -110,6 -104,7 +111,7 @@@ VERBOSE OK_TO_SKIP_PRE_REBASE= REBASE_ROOT= AUTOSQUASH= + NEVER_FF= GIT_CHERRY_PICK_HELP=" After resolving the conflicts, mark the corrected paths with 'git add ', and @@@ -205,7 -200,6 +207,7 @@@ make_patch () } die_with_patch () { + echo "$1" > "$DOTEST"/stopped-sha make_patch "$1" git rerere die "$2" @@@ -230,8 -224,8 +232,9 @@@ do_with_author () } pick_one () { - no_ff=$NEVER_FF - case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac + ff=--ff + case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac ++ case "$NEVER_FF" in '') ;; ?*) ff= ;; esac output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" test -d "$REWRITTEN" && pick_one_preserving_merges "$@" && return @@@ -240,7 -234,16 +243,7 @@@ output git cherry-pick "$@" return fi - 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" && test "$current_sha1" = "$parent_sha1" - then - output git reset --hard $sha1 - output warn Fast-forward to $(git rev-parse --short $sha1) - else - output git cherry-pick "$@" - fi + output git cherry-pick $ff "$@" } pick_one_preserving_merges () { @@@ -347,7 -350,6 +350,7 @@@ printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG die_with_patch $sha1 "Error redoing merge $sha1" fi + echo "$sha1 $(git rev-parse HEAD^0)" >> "$REWRITTEN_LIST" ;; *) output git cherry-pick "$@" || @@@ -425,26 -427,6 +428,26 @@@ die_failed_squash() die_with_patch $1 "" } +flush_rewritten_pending() { + test -s "$REWRITTEN_PENDING" || return + newsha1="$(git rev-parse HEAD^0)" + sed "s/$/ $newsha1/" < "$REWRITTEN_PENDING" >> "$REWRITTEN_LIST" + rm -f "$REWRITTEN_PENDING" +} + +record_in_rewritten() { + oldsha1="$(git rev-parse $1)" + echo "$oldsha1" >> "$REWRITTEN_PENDING" + + case "$(peek_next_command)" in + squash|s|fixup|f) + ;; + *) + flush_rewritten_pending + ;; + esac +} + do_next () { rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit read command sha1 rest < "$TODO" @@@ -458,7 -440,6 +461,7 @@@ mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" + record_in_rewritten $sha1 ;; reword|r) comment_for_reflog reword @@@ -466,8 -447,7 +469,8 @@@ mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" - git commit --amend + git commit --amend --no-post-rewrite + record_in_rewritten $sha1 ;; edit|e) comment_for_reflog edit @@@ -475,7 -455,6 +478,7 @@@ mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" + echo "$sha1" > "$DOTEST"/stopped-sha make_patch $sha1 git rev-parse --verify HEAD > "$AMEND" warn "Stopped at $sha1... $rest" @@@ -532,7 -511,6 +535,7 @@@ rm -f "$SQUASH_MSG" "$FIXUP_MSG" ;; esac + record_in_rewritten $sha1 ;; *) warn "Unknown command: $command $sha1 $rest" @@@ -561,15 -539,6 +564,15 @@@ test ! -f "$DOTEST"/verbose || git diff-tree --stat $(cat "$DOTEST"/head)..HEAD } && + { + 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" + true # we don't care if this hook failed + fi && rm -rf "$DOTEST" && git gc --auto && warn "Successfully rebased and updated $HEADNAME." @@@ -604,12 -573,7 +607,12 @@@ skip_unnecessary_picks () esac echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd done <"$TODO" >"$TODO.new" 3>>"$DONE" && - mv -f "$TODO".new "$TODO" || + mv -f "$TODO".new "$TODO" && + case "$(peek_next_command)" in + squash|s|fixup|f) + record_in_rewritten "$ONTO" + ;; + esac || die "Could not skip unnecessary pick commands" } @@@ -725,8 -689,6 +728,8 @@@ first and then run 'git rebase --contin } fi + record_in_rewritten "$(cat "$DOTEST"/stopped-sha)" + require_clean_work_tree do_rest ;; @@@ -782,6 -744,9 +785,9 @@@ -i) # yeah, we know ;; + --no-ff) + NEVER_FF=t + ;; --root) REBASE_ROOT=t ;; @@@ -823,6 -788,8 +829,6 @@@ if test ! -z "$1" then - output git show-ref --verify --quiet "refs/heads/$1" || - die "Invalid branchname: $1" output git checkout "$1" || die "Could not checkout $1" fi @@@ -965,7 -932,7 +971,7 @@@ EO has_action "$TODO" || die_abort "Nothing to do" - test -d "$REWRITTEN" || skip_unnecessary_picks + test -d "$REWRITTEN" || test -n "$NEVER_FF" || skip_unnecessary_picks git update-ref ORIG_HEAD $HEAD output git checkout $ONTO && do_rest diff --combined git-rebase.sh index e0eb9568f3,8b23f8b7d2..44f5c65fdb --- a/git-rebase.sh +++ b/git-rebase.sh @@@ -3,7 -3,7 +3,7 @@@ # Copyright (c) 2005 Junio C Hamano. # - USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto ] [|--root] [] [--quiet | -q]' + USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto ] [|--root] [] [--quiet | -q]' LONG_USAGE='git-rebase replaces with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to , otherwise it is equal to @@@ -79,7 -79,6 +79,7 @@@ continue_merge () then printf "Committed: %0${prec}d " $msgnum fi + echo "$cmt $(git rev-parse HEAD^0)" >> "$dotest/rewritten" else if test -z "$GIT_QUIET" then @@@ -152,11 -151,6 +152,11 @@@ move_to_original_branch () finish_rb_merge () { move_to_original_branch + git notes copy --for-rewrite=rebase < "$dotest"/rewritten + if test -x "$GIT_DIR"/hooks/post-rewrite && + test -s "$dotest"/rewritten; then + "$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten + fi rm -r "$dotest" say All done. } @@@ -353,7 -347,7 +353,7 @@@ d --root) rebase_root=t ;; - -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase) + -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase|--no-ff) force_rebase=t ;; --rerere-autoupdate|--no-rerere-autoupdate) diff --combined t/t3404-rebase-interactive.sh index b0b43c6d32,624e78e982..f20ea38411 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@@ -22,12 -22,18 +22,18 @@@ set_fake_edito # | \ # | F - G - H (branch1) # | \ - # \ I (branch2) - # \ - # J - K - L - M (no-conflict-branch) + # |\ I (branch2) + # | \ + # | J - K - L - M (no-conflict-branch) + # \ + # N - O - P (no-ff-branch) # # where A, B, D and G all touch file1, and one, two, three, four all # touch file "conflict". + # + # WARNING: Modifications to the initial repository can change the SHA ID used + # in the expect2 file for the 'stop on conflicting pick' test. + test_expect_success 'setup' ' test_commit A file1 && @@@ -48,6 -54,11 +54,11 @@@ done && git checkout -b no-conflict-branch A && for n in J K L M + do + test_commit $n file$n + done && + git checkout -b no-ff-branch A && + for n in N O P do test_commit $n file$n done @@@ -113,7 -124,7 +124,7 @@@ cat > expect2 << EO D ======= G - >>>>>>> 51047de... G + >>>>>>> 5d18e54... G EOF test_expect_success 'stop on conflicting pick' ' @@@ -553,37 -564,21 +564,54 @@@ test_expect_success 'reword' git show HEAD~2 | grep "C changed" ' +test_expect_success 'rebase -i can copy notes' ' + git config notes.rewrite.rebase true && + git config notes.rewriteRef "refs/notes/*" && + test_commit n1 && + test_commit n2 && + test_commit n3 && + git notes add -m"a note" n3 && + git rebase --onto n1 n2 && + test "a note" = "$(git notes show HEAD)" +' + +cat >expect < output && + test_cmp expect output +' + +test_expect_success 'rebase while detaching HEAD' ' + git symbolic-ref HEAD && + grandparent=$(git rev-parse HEAD~2) && + test_tick && + FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 && + test $grandparent = $(git rev-parse HEAD~2) && + test_must_fail git symbolic-ref HEAD +' + + test_tick # Ensure that the rebased commits get a different timestamp. + test_expect_success 'always cherry-pick with --no-ff' ' + git checkout no-ff-branch && + git tag original-no-ff-branch && + git rebase -i --no-ff A && + touch empty && + for p in 0 1 2 + do + test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) && + git diff HEAD~$p original-no-ff-branch~$p > out && + test_cmp empty out + done && + test $(git rev-parse HEAD~3) = $(git rev-parse original-no-ff-branch~3) && + git diff HEAD~3 original-no-ff-branch~3 > out && + test_cmp empty out + ' + test_done