Teach a new option, --autosquash, to the interactive rebase. When the commit log message begins with "!fixup ...", and there is a commit whose title begins with the same ..., automatically modify the todo list of rebase -i so that the commit marked for squashing come right after the commit to be modified, and change the action of the moved commit from pick to squash. Signed-off-by: Nanako Shiraishi <nanako3@xxxxxxxxxxx> --- Junio C Hamano <gitster@xxxxxxxxx> writes: > If Michael rolls his second round with your "--autosquash", or you do so > yourself on top of his patch, I think it _might_ be safer to mark the ones > automatically moved as "squash", and not as "fix", and have the users > explicitly change the "squash" they want to "fix" themselves. > Alternatively, you can also use two magic tokens (i.e. instead of one > "fixup!", allow people to use "squash!" and "fixup!") and change the > action chosen for the moved commits to "squash" and "fixup" respectively. Here is a rebased and updated version of my patch from June 2009. It should apply cleanly on top of Michael's patch. Documentation/git-rebase.txt | 10 +++++++ git-rebase--interactive.sh | 43 +++++++++++++++++++++++++++++++ t/t3415-rebase-autosquash.sh | 58 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 0 deletions(-) create mode 100755 t/t3415-rebase-autosquash.sh diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 9b648ec..87cb62d 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -308,6 +308,16 @@ which makes little sense. root commits will be rewritten to have <newbase> as parent instead. +--autosquash:: + When the commit log message begins with "!squash ..." (or + "!fixup ..."), and there is a commit whose title begins with + the same ..., automatically modify the todo list of rebase -i + so that the commit marked for quashing come right after the + commit to be modified, and change the action of the moved + commit from `pick` to `squash` (or `fixup`). ++ +This option is only valid when '--interactive' option is used. + include::merge-strategies.txt[] NOTES diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 30de96e..b014231 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 " . git-sh-setup @@ -46,6 +47,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 <paths>', and @@ -519,6 +521,43 @@ 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 + + sed -e '/^pick [0-9a-f]* !squash /d' \ + -e '/^pick [0-9a-f]* !fixup /d' \ + "$1" | + ( + used= + while read pick sha1 message + do + echo "$pick $sha1 $message" + while read squash action msg + do + case " $used" in + *" $squash "*) + continue ;; + esac + case "$message" in + "$msg"*) + echo "$action $squash !$action $msg" + used="$used$squash " + ;; + esac + done <"$1.sq" + done >"$1.rearranged" + ) + cat "$1.rearranged" >"$1" + rm -f "$1.sq" +} + while test $# != 0 do case "$1" in @@ -624,6 +663,9 @@ first and then run 'git rebase --continue' again." --root) REBASE_ROOT=t ;; + --autosquash) + AUTOSQUASH=t + ;; --onto) shift ONTO=$(git rev-parse --verify "$1") || @@ -783,6 +825,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 diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh new file mode 100755 index 0000000..5ea2073 --- /dev/null +++ b/t/t3415-rebase-autosquash.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +test_description='auto squash' + +. ./test-lib.sh + +test_expect_success setup ' + echo 0 > file0 && + git add . && + test_tick && + git commit -m "initial commit" && + echo 0 > file1 && + echo 2 > file2 && + git add . && + test_tick && + git commit -m "first commit" && + echo 3 > file3 && + git add . && + test_tick && + git commit -m "second commit" && + git tag base +' + +test_expect_success 'auto fixup' ' + git reset --hard base && + echo 1 > file1 && + git add -u && + test_tick && + git commit -m "!fixup first" + + git tag final-fixup && + test_tick && + git rebase --autosquash -i HEAD^^^ && + git log --oneline >actual && + test 3 = $(wc -l <actual) && + git diff --exit-code final-fixup && + test 1 = "$(git cat-file blob HEAD^:file1)" && + test 1 = $(git cat-file commit HEAD^ | grep first | wc -l) +' + +test_expect_success 'auto squash' ' + git reset --hard base && + echo 1 > file1 && + git add -u && + test_tick && + git commit -m "!squash first" + + git tag final-squash && + test_tick && + git rebase --autosquash -i HEAD^^^ && + git log --oneline >actual && + test 3 = $(wc -l <actual) && + git diff --exit-code final-squash && + test 1 = "$(git cat-file blob HEAD^:file1)" && + test 2 = $(git cat-file commit HEAD^ | grep first | wc -l) +' + +test_done -- 1.6.6.rc0.60.g4926 -- Nanako Shiraishi http://ivory.ap.teacup.com/nanako3/ -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html