rebase -i: invoke post-rewrite hook

Aside from the same issue that rebase also has (remembering the
original commit across a conflict resolution), rebase -i brings an
extra twist: We need to defer writing the rewritten list in the case
of {squash,fixup} because their rewritten result should be the last
commit in the squashed group.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Thomas Rast 2010-03-12 18:04:30 +01:00 committed by Junio C Hamano
parent 96e19488f1
commit b079feed64
2 changed files with 146 additions and 1 deletions

View File

@ -96,6 +96,13 @@ AUTHOR_SCRIPT="$DOTEST"/author-script
# command is processed, this file is deleted. # command is processed, this file is deleted.
AMEND="$DOTEST"/amend 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= PRESERVE_MERGES=
STRATEGY= STRATEGY=
ONTO= ONTO=
@ -198,6 +205,7 @@ make_patch () {
} }
die_with_patch () { die_with_patch () {
echo "$1" > "$DOTEST"/stopped-sha
make_patch "$1" make_patch "$1"
git rerere git rerere
die "$2" die "$2"
@ -348,6 +356,7 @@ pick_one_preserving_merges () {
printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
die_with_patch $sha1 "Error redoing merge $sha1" die_with_patch $sha1 "Error redoing merge $sha1"
fi fi
echo "$sha1 $(git rev-parse HEAD^0)" >> "$REWRITTEN_LIST"
;; ;;
*) *)
output git cherry-pick "$@" || output git cherry-pick "$@" ||
@ -425,6 +434,26 @@ die_failed_squash() {
die_with_patch $1 "" 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 () { do_next () {
rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
read command sha1 rest < "$TODO" read command sha1 rest < "$TODO"
@ -438,6 +467,7 @@ do_next () {
mark_action_done mark_action_done
pick_one $sha1 || pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest" die_with_patch $sha1 "Could not apply $sha1... $rest"
record_in_rewritten $sha1
;; ;;
reword|r) reword|r)
comment_for_reflog reword comment_for_reflog reword
@ -446,6 +476,7 @@ do_next () {
pick_one $sha1 || pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest" die_with_patch $sha1 "Could not apply $sha1... $rest"
git commit --amend --no-post-rewrite git commit --amend --no-post-rewrite
record_in_rewritten $sha1
;; ;;
edit|e) edit|e)
comment_for_reflog edit comment_for_reflog edit
@ -453,6 +484,7 @@ do_next () {
mark_action_done mark_action_done
pick_one $sha1 || pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest" die_with_patch $sha1 "Could not apply $sha1... $rest"
echo "$1" > "$DOTEST"/stopped-sha
make_patch $sha1 make_patch $sha1
git rev-parse --verify HEAD > "$AMEND" git rev-parse --verify HEAD > "$AMEND"
warn "Stopped at $sha1... $rest" warn "Stopped at $sha1... $rest"
@ -509,6 +541,7 @@ do_next () {
rm -f "$SQUASH_MSG" "$FIXUP_MSG" rm -f "$SQUASH_MSG" "$FIXUP_MSG"
;; ;;
esac esac
record_in_rewritten $sha1
;; ;;
*) *)
warn "Unknown command: $command $sha1 $rest" warn "Unknown command: $command $sha1 $rest"
@ -537,6 +570,11 @@ do_next () {
test ! -f "$DOTEST"/verbose || test ! -f "$DOTEST"/verbose ||
git diff-tree --stat $(cat "$DOTEST"/head)..HEAD git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
} && } &&
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" && rm -rf "$DOTEST" &&
git gc --auto && git gc --auto &&
warn "Successfully rebased and updated $HEADNAME." warn "Successfully rebased and updated $HEADNAME."
@ -571,7 +609,12 @@ skip_unnecessary_picks () {
esac esac
echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
done <"$TODO" >"$TODO.new" 3>>"$DONE" && 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" die "Could not skip unnecessary pick commands"
} }
@ -685,6 +728,7 @@ first and then run 'git rebase --continue' again."
test -n "$amend" && git reset --soft $amend test -n "$amend" && git reset --soft $amend
die "Could not commit staged changes." die "Could not commit staged changes."
} }
record_in_rewritten "$(cat "$DOTEST"/stopped-sha)"
fi fi
require_clean_work_tree require_clean_work_tree

View File

@ -79,4 +79,105 @@ EOF
verify_hook_input verify_hook_input
' '
test_expect_success 'git rebase -m' '
git reset --hard D &&
clear_hook_input &&
test_must_fail git rebase -m --onto A B &&
echo C > foo &&
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
cat >expected.data <<EOF &&
$(git rev-parse C) $(git rev-parse HEAD^)
$(git rev-parse D) $(git rev-parse HEAD)
EOF
verify_hook_input
'
test_expect_success 'git rebase -m --skip' '
git reset --hard D &&
clear_hook_input &&
test_must_fail git rebase --onto A B &&
test_must_fail git rebase --skip &&
echo D > foo &&
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
cat >expected.data <<EOF &&
$(git rev-parse D) $(git rev-parse HEAD)
EOF
verify_hook_input
'
. "$TEST_DIRECTORY"/lib-rebase.sh
set_fake_editor
# Helper to work around the lack of one-shot exporting for
# test_must_fail (as it is a shell function)
test_fail_interactive_rebase () {
(
FAKE_LINES="$1" &&
shift &&
export FAKE_LINES &&
test_must_fail git rebase -i "$@"
)
}
test_expect_success 'git rebase -i (unchanged)' '
git reset --hard D &&
clear_hook_input &&
test_fail_interactive_rebase "1 2" --onto A B &&
echo C > foo &&
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
cat >expected.data <<EOF &&
$(git rev-parse C) $(git rev-parse HEAD^)
$(git rev-parse D) $(git rev-parse HEAD)
EOF
verify_hook_input
'
test_expect_success 'git rebase -i (skip)' '
git reset --hard D &&
clear_hook_input &&
test_fail_interactive_rebase "2" --onto A B &&
echo D > foo &&
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
cat >expected.data <<EOF &&
$(git rev-parse D) $(git rev-parse HEAD)
EOF
verify_hook_input
'
test_expect_success 'git rebase -i (squash)' '
git reset --hard D &&
clear_hook_input &&
test_fail_interactive_rebase "1 squash 2" --onto A B &&
echo C > foo &&
git add foo &&
git rebase --continue &&
echo rebase >expected.args &&
cat >expected.data <<EOF &&
$(git rev-parse C) $(git rev-parse HEAD)
$(git rev-parse D) $(git rev-parse HEAD)
EOF
verify_hook_input
'
test_expect_success 'git rebase -i (fixup without conflict)' '
git reset --hard D &&
clear_hook_input &&
FAKE_LINES="1 fixup 2" git rebase -i B &&
echo rebase >expected.args &&
cat >expected.data <<EOF &&
$(git rev-parse C) $(git rev-parse HEAD)
$(git rev-parse D) $(git rev-parse HEAD)
EOF
verify_hook_input
'
test_done test_done