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>
Thomas Rast committed
Mar 12, 2010 at 18:04 UTC
b079feed64238558fa24ba8ade892d84628a05ac
2 files changed
+146
-1
git-rebase--interactive.sh
+45
-1
@@ -96,6 +96,13 @@ AUTHOR_SCRIPT="$DOTEST"/author-script
96
# command is processed, this file is deleted.
97
AMEND="$DOTEST"/amend
98
99
+# For the post-rewrite hook, we make a list of rewritten commits and
100
+# their new sha1s. The rewritten-pending list keeps the sha1s of
101
+# commits that have been processed, but not committed yet,
102
+# e.g. because they are waiting for a 'squash' command.
103
+REWRITTEN_LIST="$DOTEST"/rewritten-list
104
+REWRITTEN_PENDING="$DOTEST"/rewritten-pending
105
+
106
PRESERVE_MERGES=
107
STRATEGY=
108
ONTO=
@@ -198,6 +205,7 @@ make_patch () {
205
}
206
207
die_with_patch () {
208
+ echo "$1" > "$DOTEST"/stopped-sha
209
make_patch "$1"
210
git rerere
211
die "$2"
@@ -348,6 +356,7 @@ pick_one_preserving_merges () {
356
printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
357
die_with_patch $sha1 "Error redoing merge $sha1"
358
fi
359
+ echo "$sha1 $(git rev-parse HEAD^0)" >> "$REWRITTEN_LIST"
360
;;
361
*)
362
output git cherry-pick "$@" ||
@@ -425,6 +434,26 @@ die_failed_squash() {
434
die_with_patch $1 ""
435
}
436
437
+flush_rewritten_pending() {
438
+ test -s "$REWRITTEN_PENDING" || return
439
+ newsha1="$(git rev-parse HEAD^0)"
440
+ sed "s/$/ $newsha1/" < "$REWRITTEN_PENDING" >> "$REWRITTEN_LIST"
441
+ rm -f "$REWRITTEN_PENDING"
442
+}
443
+
444
+record_in_rewritten() {
445
+ oldsha1="$(git rev-parse $1)"
446
+ echo "$oldsha1" >> "$REWRITTEN_PENDING"
447
+
448
+ case "$(peek_next_command)" in
449
+ squash|s|fixup|f)
450
+ ;;
451
+ *)
452
+ flush_rewritten_pending
453
+ ;;
454
+ esac
455
+}
456
+
457
do_next () {
458
rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
459
read command sha1 rest < "$TODO"
@@ -438,6 +467,7 @@ do_next () {
467
mark_action_done
468
pick_one $sha1 ||
469
die_with_patch $sha1 "Could not apply $sha1... $rest"
470
+ record_in_rewritten $sha1
471
;;
472
reword|r)
473
comment_for_reflog reword
@@ -446,6 +476,7 @@ do_next () {
476
pick_one $sha1 ||
477
die_with_patch $sha1 "Could not apply $sha1... $rest"
478
git commit --amend --no-post-rewrite
479
+ record_in_rewritten $sha1
480
;;
481
edit|e)
482
comment_for_reflog edit
@@ -453,6 +484,7 @@ do_next () {
484
mark_action_done
485
pick_one $sha1 ||
486
die_with_patch $sha1 "Could not apply $sha1... $rest"
487
+ echo "$1" > "$DOTEST"/stopped-sha
488
make_patch $sha1
489
git rev-parse --verify HEAD > "$AMEND"
490
warn "Stopped at $sha1... $rest"
@@ -509,6 +541,7 @@ do_next () {
541
rm -f "$SQUASH_MSG" "$FIXUP_MSG"
542
;;
543
esac
544
+ record_in_rewritten $sha1
545
;;
546
*)
547
warn "Unknown command: $command $sha1 $rest"
@@ -537,6 +570,11 @@ do_next () {
570
test ! -f "$DOTEST"/verbose ||
571
git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
572
} &&
573
+ if test -x "$GIT_DIR"/hooks/post-rewrite &&
574
+ test -s "$REWRITTEN_LIST"; then
575
+ "$GIT_DIR"/hooks/post-rewrite rebase < "$REWRITTEN_LIST"
576
+ true # we don't care if this hook failed
577
+ fi &&
578
rm -rf "$DOTEST" &&
579
git gc --auto &&
580
warn "Successfully rebased and updated $HEADNAME."
@@ -571,7 +609,12 @@ skip_unnecessary_picks () {
609
esac
610
echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
611
done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
574
- mv -f "$TODO".new "$TODO" ||
612
+ mv -f "$TODO".new "$TODO" &&
613
+ case "$(peek_next_command)" in
614
+ squash|s|fixup|f)
615
+ record_in_rewritten "$ONTO"
616
+ ;;
617
+ esac ||
618
die "Could not skip unnecessary pick commands"
619
}
620
@@ -685,6 +728,7 @@ first and then run 'git rebase --continue' again."
728
test -n "$amend" && git reset --soft $amend
729
die "Could not commit staged changes."
730
}
731
+ record_in_rewritten "$(cat "$DOTEST"/stopped-sha)"
732
fi
733
734
require_clean_work_tree
t/t5407-post-rewrite-hook.sh
+101
@@ -79,4 +79,105 @@ EOF
79
verify_hook_input
80
'
81
82
+test_expect_success 'git rebase -m' '
83
+ git reset --hard D &&
84
+ clear_hook_input &&
85
+ test_must_fail git rebase -m --onto A B &&
86
+ echo C > foo &&
87
+ git add foo &&
88
+ git rebase --continue &&
89
+ echo rebase >expected.args &&
90
+ cat >expected.data <<EOF &&
91
+$(git rev-parse C) $(git rev-parse HEAD^)
92
+$(git rev-parse D) $(git rev-parse HEAD)
93
+EOF
94
+ verify_hook_input
95
+'
96
+
97
+test_expect_success 'git rebase -m --skip' '
98
+ git reset --hard D &&
99
+ clear_hook_input &&
100
+ test_must_fail git rebase --onto A B &&
101
+ test_must_fail git rebase --skip &&
102
+ echo D > foo &&
103
+ git add foo &&
104
+ git rebase --continue &&
105
+ echo rebase >expected.args &&
106
+ cat >expected.data <<EOF &&
107
+$(git rev-parse D) $(git rev-parse HEAD)
108
+EOF
109
+ verify_hook_input
110
+'
111
+
112
+. "$TEST_DIRECTORY"/lib-rebase.sh
113
+
114
+set_fake_editor
115
+
116
+# Helper to work around the lack of one-shot exporting for
117
+# test_must_fail (as it is a shell function)
118
+test_fail_interactive_rebase () {
119
+ (
120
+ FAKE_LINES="$1" &&
121
+ shift &&
122
+ export FAKE_LINES &&
123
+ test_must_fail git rebase -i "$@"
124
+ )
125
+}
126
+
127
+test_expect_success 'git rebase -i (unchanged)' '
128
+ git reset --hard D &&
129
+ clear_hook_input &&
130
+ test_fail_interactive_rebase "1 2" --onto A B &&
131
+ echo C > foo &&
132
+ git add foo &&
133
+ git rebase --continue &&
134
+ echo rebase >expected.args &&
135
+ cat >expected.data <<EOF &&
136
+$(git rev-parse C) $(git rev-parse HEAD^)
137
+$(git rev-parse D) $(git rev-parse HEAD)
138
+EOF
139
+ verify_hook_input
140
+'
141
+
142
+test_expect_success 'git rebase -i (skip)' '
143
+ git reset --hard D &&
144
+ clear_hook_input &&
145
+ test_fail_interactive_rebase "2" --onto A B &&
146
+ echo D > foo &&
147
+ git add foo &&
148
+ git rebase --continue &&
149
+ echo rebase >expected.args &&
150
+ cat >expected.data <<EOF &&
151
+$(git rev-parse D) $(git rev-parse HEAD)
152
+EOF
153
+ verify_hook_input
154
+'
155
+
156
+test_expect_success 'git rebase -i (squash)' '
157
+ git reset --hard D &&
158
+ clear_hook_input &&
159
+ test_fail_interactive_rebase "1 squash 2" --onto A B &&
160
+ echo C > foo &&
161
+ git add foo &&
162
+ git rebase --continue &&
163
+ echo rebase >expected.args &&
164
+ cat >expected.data <<EOF &&
165
+$(git rev-parse C) $(git rev-parse HEAD)
166
+$(git rev-parse D) $(git rev-parse HEAD)
167
+EOF
168
+ verify_hook_input
169
+'
170
+
171
+test_expect_success 'git rebase -i (fixup without conflict)' '
172
+ git reset --hard D &&
173
+ clear_hook_input &&
174
+ FAKE_LINES="1 fixup 2" git rebase -i B &&
175
+ echo rebase >expected.args &&
176
+ cat >expected.data <<EOF &&
177
+$(git rev-parse C) $(git rev-parse HEAD)
178
+$(git rev-parse D) $(git rev-parse HEAD)
179
+EOF
180
+ verify_hook_input
181
+'
182
+
183
test_done