Merge branch 'ap/rebase-multiple-fixups'

Having multiple "fixup!" on a line in the rebase instruction sheet
did not work very well with "git rebase -i --autosquash".

* ap/rebase-multiple-fixups:
  lib-rebase: style: use write_script, <<-\EOF
  rebase -i: handle fixup! fixup! in --autosquash
This commit is contained in:
Junio C Hamano 2013-07-01 12:41:52 -07:00
commit eb3a4fc149
4 changed files with 130 additions and 44 deletions

View File

@ -389,7 +389,9 @@ squash/fixup series.
the same ..., automatically modify the todo list of rebase -i the same ..., automatically modify the todo list of rebase -i
so that the commit marked for squashing comes right after the so that the commit marked for squashing comes right after the
commit to be modified, and change the action of the moved commit to be modified, and change the action of the moved
commit from `pick` to `squash` (or `fixup`). commit from `pick` to `squash` (or `fixup`). Ignores subsequent
"fixup! " or "squash! " after the first, in case you referred to an
earlier fixup/squash with `git commit --fixup/--squash`.
+ +
This option is only valid when the '--interactive' option is used. This option is only valid when the '--interactive' option is used.
+ +

View File

@ -689,8 +689,22 @@ rearrange_squash () {
case "$message" in case "$message" in
"squash! "*|"fixup! "*) "squash! "*|"fixup! "*)
action="${message%%!*}" action="${message%%!*}"
rest="${message#*! }" rest=$message
echo "$sha1 $action $rest" 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
echo "$sha1 $action $prefix $rest"
# if it's a single word, try to resolve to a full sha1 and # 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 # emit a second copy. This allows us to match on both message
# and on sha1 prefix # and on sha1 prefix
@ -699,7 +713,7 @@ rearrange_squash () {
if test -n "$fullsha"; then if test -n "$fullsha"; then
# prefix the action to uniquely identify this line as # prefix the action to uniquely identify this line as
# intended for full sha1 match # intended for full sha1 match
echo "$sha1 +$action $fullsha" echo "$sha1 +$action $prefix $fullsha"
fi fi
fi fi
esac esac
@ -714,7 +728,7 @@ rearrange_squash () {
esac esac
printf '%s\n' "$pick $sha1 $message" printf '%s\n' "$pick $sha1 $message"
used="$used$sha1 " used="$used$sha1 "
while read -r squash action msg_content while read -r squash action msg_prefix msg_content
do do
case " $used" in case " $used" in
*" $squash "*) continue ;; *" $squash "*) continue ;;
@ -730,7 +744,8 @@ rearrange_squash () {
case "$message" in "$msg_content"*) emit=1;; esac ;; case "$message" in "$msg_content"*) emit=1;; esac ;;
esac esac
if test $emit = 1; then 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 " used="$used$squash "
fi fi
done <"$1.sq" done <"$1.sq"

View File

@ -22,48 +22,60 @@
# ">" -- Add a blank line. # ">" -- Add a blank line.
set_fake_editor () { set_fake_editor () {
echo "#!$SHELL_PATH" >fake-editor.sh write_script fake-editor.sh <<-\EOF
cat >> fake-editor.sh <<\EOF case "$1" in
case "$1" in */COMMIT_EDITMSG)
*/COMMIT_EDITMSG) test -z "$EXPECT_HEADER_COUNT" ||
test -z "$EXPECT_HEADER_COUNT" || test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" || exit
test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
exit exit
test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1" ;;
test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
exit
;;
esac
test -z "$EXPECT_COUNT" ||
test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
exit
test -z "$FAKE_LINES" && exit
grep -v '^#' < "$1" > "$1".tmp
rm -f "$1"
echo 'rebase -i script before editing:'
cat "$1".tmp
action=pick
for line in $FAKE_LINES; do
case $line in
squash|fixup|edit|reword)
action="$line";;
exec*)
echo "$line" | sed 's/_/ /g' >> "$1";;
"#")
echo '# comment' >> "$1";;
">")
echo >> "$1";;
*)
sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
action=pick;;
esac esac
done test -z "$EXPECT_COUNT" ||
echo 'rebase -i script after editing:' test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
cat "$1" exit
EOF test -z "$FAKE_LINES" && exit
grep -v '^#' < "$1" > "$1".tmp
rm -f "$1"
echo 'rebase -i script before editing:'
cat "$1".tmp
action=pick
for line in $FAKE_LINES; do
case $line in
squash|fixup|edit|reword)
action="$line";;
exec*)
echo "$line" | sed 's/_/ /g' >> "$1";;
"#")
echo '# comment' >> "$1";;
">")
echo >> "$1";;
*)
sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
action=pick;;
esac
done
echo 'rebase -i script after editing:'
cat "$1"
EOF
test_set_editor "$(pwd)/fake-editor.sh" test_set_editor "$(pwd)/fake-editor.sh"
chmod a+x fake-editor.sh }
# After set_cat_todo_editor, rebase -i will write the todo list (ignoring
# blank lines and comments) to stdout, and exit failure (so you should run
# it with test_must_fail). This can be used to verify the expected user
# experience, for todo list changes that do not affect the outcome of
# rebase; or as an extra check in addition to checking the outcome.
set_cat_todo_editor () {
write_script fake-editor.sh <<-\EOF
grep "^[^#]" "$1"
exit 1
EOF
test_set_editor "$(pwd)/fake-editor.sh"
} }
# checks that the revisions in "$2" represent a linear range with the # checks that the revisions in "$2" represent a linear range with the

View File

@ -4,6 +4,8 @@ test_description='auto squash'
. ./test-lib.sh . ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
test_expect_success setup ' test_expect_success setup '
echo 0 >file0 && echo 0 >file0 &&
git add . && git add . &&
@ -193,4 +195,59 @@ test_expect_success 'use commit --squash' '
test_auto_commit_flags squash 2 test_auto_commit_flags squash 2
' '
test_auto_fixup_fixup () {
git reset --hard base &&
echo 1 >file1 &&
git add -u &&
test_tick &&
git commit -m "$1! first" &&
echo 2 >file1 &&
git add -u &&
test_tick &&
git commit -m "$1! $2! first" &&
git tag "final-$1-$2" &&
test_tick &&
(
set_cat_todo_editor &&
test_must_fail git rebase --autosquash -i HEAD^^^^ >actual &&
cat >expected <<-EOF &&
pick $(git rev-parse --short HEAD^^^) first commit
$1 $(git rev-parse --short HEAD^) $1! first
$1 $(git rev-parse --short HEAD) $1! $2! first
pick $(git rev-parse --short HEAD^^) second commit
EOF
test_cmp expected actual
) &&
git rebase --autosquash -i HEAD^^^^ &&
git log --oneline >actual &&
test_line_count = 3 actual
git diff --exit-code "final-$1-$2" &&
test 2 = "$(git cat-file blob HEAD^:file1)" &&
if test "$1" = "fixup"
then
test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
elif test "$1" = "squash"
then
test 3 = $(git cat-file commit HEAD^ | grep first | wc -l)
else
false
fi
}
test_expect_success 'fixup! fixup!' '
test_auto_fixup_fixup fixup fixup
'
test_expect_success 'fixup! squash!' '
test_auto_fixup_fixup fixup squash
'
test_expect_success 'squash! squash!' '
test_auto_fixup_fixup squash squash
'
test_expect_success 'squash! fixup!' '
test_auto_fixup_fixup squash fixup
'
test_done test_done