Skip to content

Commit 4db12b6

Browse files
committed
rebase -i: cross-validate rebase--helper's functionality
Inspired by GitHub's "Scientist" library (and by this developer's validation of the builtin replacement for RCS merge, back in the days), we run things *twice*: once with the original shell script code, and once with the new rebase--helper method. Note that this will take a toll on users for now: merge conflicts and rewords have to be addressed twice, too, and care needs to be taken that exec commands (and copying commit notes) can be performed twice; If not, the environment variable GIT_USE_REBASE_HELPER=true needs to be set (or, in case of breakage, it needs to be set to "false" before rerunning the rebase). Please note that for the same reason, some tests fail with merge conflicts, or specifically test amending commits via "reword" or "edit" commands. Naturally, these tests cannot be cross-validated easily, because the same "interactive" commands would have to be run again. So let's just run the rebase--helper method to verify that these tests still pass. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent b140632 commit 4db12b6

File tree

4 files changed

+197
-55
lines changed

4 files changed

+197
-55
lines changed

git-rebase--interactive.sh

Lines changed: 135 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
test ! -z "$GIT_USE_REBASE_HELPER" ||
2+
GIT_USE_REBASE_HELPER=cross-validate
3+
14
# This shell script fragment is sourced by git-rebase to implement
25
# its interactive mode. "git rebase --interactive" makes it easy
36
# to fix up commits in the middle of a series and rearrange commits.
@@ -677,6 +680,35 @@ do_next () {
677680
fi &&
678681
warn "Successfully rebased and updated $head_name."
679682

683+
# Run the same shebang using rebase--helper
684+
if test cross-validate = "$GIT_USE_REBASE_HELPER" &&
685+
test -d "$GIT_DIR"/saved-rebase-merge
686+
then
687+
rm -rf "$GIT_DIR"/shell-rebase-merge
688+
689+
echo "Cross-validating with rebase--helper" >&2
690+
mv "$GIT_DIR"/rebase-merge "$GIT_DIR"/shell-rebase-merge &&
691+
cp -R "$GIT_DIR"/saved-rebase-merge "$GIT_DIR"/rebase-merge &&
692+
echo 1 >"$GIT_DIR"/saved-rebase-merge/cross-validating &&
693+
git rev-parse HEAD >"$GIT_DIR"/saved-rebase-merge/shell-head &&
694+
output git checkout \
695+
$(cat "$GIT_DIR"/saved-rebase-merge/start-head) &&
696+
case "$(cat "$GIT_DIR"/rebase-merge/head-name)" in
697+
refs/*) git update-ref -m "re-run rebase" \
698+
$(cat "$GIT_DIR"/rebase-merge/head-name) \
699+
$(cat "$GIT_DIR"/rebase-merge/orig-head)
700+
;;
701+
esac &&
702+
git rebase--helper ${force_rebase:+--no-ff} --continue ||
703+
die "Builtin rebase--helper failed"
704+
if test -f "$todo"
705+
then
706+
exit
707+
else
708+
check_rebase__helper
709+
fi
710+
fi
711+
680712
return 1 # not failure; just to break the do_rest loop
681713
}
682714

@@ -744,11 +776,27 @@ transform_todo_ids () {
744776
}
745777

746778
expand_todo_ids() {
779+
cp "$todo" "$todo".transform
780+
transform_todo_ids
781+
mv "$todo" "$todo".transformed
782+
cp "$todo".transform "$todo"
747783
git rebase--helper --expand-sha1s
784+
if ! git diff --no-index -w -- "$todo" "$todo.transformed"
785+
then
786+
die "rebase--helper failed to expand todo IDs"
787+
fi
748788
}
749789

750790
collapse_todo_ids() {
791+
cp "$todo" "$todo".transform
792+
transform_todo_ids --short
793+
mv "$todo" "$todo".transformed
794+
cp "$todo".transform "$todo"
751795
git rebase--helper --shorten-sha1s
796+
if ! git diff --no-index -w -- "$todo" "$todo.transformed"
797+
then
798+
die "rebase--helper failed to collapse todo IDs"
799+
fi
752800
}
753801

754802
# Rearrange the todo list that has both "pick sha1 msg" and
@@ -1033,6 +1081,23 @@ check_todo_list () {
10331081
fi
10341082
}
10351083

1084+
check_rebase__helper () {
1085+
echo "Cross-validating rebase--helper's result" >&2
1086+
test -f "$GIT_DIR"/saved-rebase-merge/cross-validating ||
1087+
return 0
1088+
rm -rf "$GIT_DIR"/before-rebase-merge
1089+
mv "$GIT_DIR"/saved-rebase-merge "$GIT_DIR"/before-rebase-merge
1090+
start_head=$(cat "$GIT_DIR"/before-rebase-merge/start-head)
1091+
shell_head=$(cat "$GIT_DIR"/before-rebase-merge/shell-head)
1092+
git rev-parse HEAD >"$GIT_DIR"/before-rebase-merge/builtin-head
1093+
git log --raw --date-order $start_head..$shell_head |
1094+
sed "s/^commit .*$/commit/" >"$GIT_DIR"/before-rebase-merge/shell_log
1095+
git log --raw --date-order $start_head.. |
1096+
sed "s/^commit .*$/commit/" >"$GIT_DIR"/before-rebase-merge/builtin_log
1097+
cmp "$GIT_DIR"/before-rebase-merge/shell_log "$GIT_DIR"/before-rebase-merge/builtin_log ||
1098+
die "Builtin rebase--helper produced different result"
1099+
}
1100+
10361101
# The whole contents of this file is run by dot-sourcing it from
10371102
# inside a shell function. It used to be that "return"s we see
10381103
# below were not inside any function, and expected to return
@@ -1046,10 +1111,21 @@ git_rebase__interactive () {
10461111

10471112
case "$action" in
10481113
continue)
1049-
if test ! -d "$rewritten"
1114+
if test -f "$GIT_DIR"/saved-rebase-merge/cross-validating
1115+
then
1116+
git rebase--helper ${force_rebase:+--no-ff} --continue &&
1117+
if test ! -f "$todo"
1118+
then
1119+
check_rebase__helper
1120+
fi
1121+
exit
1122+
fi
1123+
1124+
if test -f "$GIT_DIR"/rebase-merge/use-builtin
10501125
then
10511126
exec git rebase--helper ${force_rebase:+--no-ff} --continue
10521127
fi
1128+
10531129
# do we have anything to commit?
10541130
if git diff-index --cached --quiet HEAD --
10551131
then
@@ -1107,10 +1183,21 @@ first and then run 'git rebase --continue' again."
11071183
skip)
11081184
git rerere clear
11091185

1110-
if test ! -d "$rewritten"
1186+
if test -f "$GIT_DIR"/saved-rebase-merge/cross-validating
1187+
then
1188+
git rebase--helper ${force_rebase:+--no-ff} --continue &&
1189+
if test ! -f "$todo"
1190+
then
1191+
check_rebase__helper
1192+
fi
1193+
exit
1194+
fi
1195+
1196+
if test -f "$GIT_DIR"/rebase-merge/use-builtin
11111197
then
11121198
exec git rebase--helper ${force_rebase:+--no-ff} --continue
11131199
fi
1200+
11141201
do_rest
11151202
return 0
11161203
;;
@@ -1189,27 +1276,25 @@ else
11891276
revisions=$onto...$orig_head
11901277
shortrevisions=$shorthead
11911278
fi
1192-
if test t != "$preserve_merges"
1193-
then
1194-
git rebase--helper --make-script ${keep_empty:+--keep-empty} \
1195-
$revisions ${restrict_revision+^$restrict_revision} > "$todo"
1196-
else
1197-
format=$(git config --get rebase.instructionFormat)
1198-
# the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
1199-
git rev-list $merges_option --format="%m%H ${format:-%s}" \
1200-
--reverse --left-right --topo-order \
1201-
$revisions ${restrict_revision+^$restrict_revision} | \
1202-
sed -n "s/^>//p" |
1203-
while read -r sha1 rest
1204-
do
1205-
1206-
if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
1207-
then
1208-
comment_out="$comment_char "
1209-
else
1210-
comment_out=
1211-
fi
1279+
format=$(git config --get rebase.instructionFormat)
1280+
# the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
1281+
git rev-list $merges_option --format="%m%H ${format:-%s}" \
1282+
--reverse --left-right --topo-order \
1283+
$revisions ${restrict_revision+^$restrict_revision} | \
1284+
sed -n "s/^>//p" |
1285+
while read -r sha1 rest
1286+
do
1287+
if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
1288+
then
1289+
comment_out="$comment_char "
1290+
else
1291+
comment_out=
1292+
fi
12121293

1294+
if test t != "$preserve_merges"
1295+
then
1296+
printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
1297+
else
12131298
if test -z "$rebase_root"
12141299
then
12151300
preserve=t
@@ -1228,7 +1313,23 @@ else
12281313
touch "$rewritten"/$sha1
12291314
printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
12301315
fi
1231-
done
1316+
fi
1317+
done
1318+
if test -z "$rebase_root" && test ! -d "$rewritten"
1319+
then
1320+
git rebase--helper --make-script ${keep_empty:+--keep-empty} \
1321+
$revisions ${restrict_revision+^$restrict_revision} \
1322+
>"$todo".helped
1323+
if test ! -s "$todo".helped
1324+
then
1325+
test ! -f "$todo" || die "$todo.helped should be empty"
1326+
elif ! cmp "$todo" "$todo".helped
1327+
then
1328+
echo git rebase--helper --make-script \
1329+
${keep_empty:+--keep-empty} $revisions \
1330+
${restrict_revision+^$restrict_revision} >"$todo".gen
1331+
die "make-script generated something incompatible"
1332+
fi
12321333
fi
12331334

12341335
# Watch for commits that been dropped by --cherry-pick
@@ -1300,10 +1401,20 @@ expand_todo_ids
13001401
test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
13011402

13021403
checkout_onto
1303-
if test -z "$rebase_root" && test ! -d "$rewritten"
1404+
if test true = "$GIT_USE_REBASE_HELPER" && test -z "$rebase_root" &&
1405+
test ! -d "$rewritten"
13041406
then
13051407
require_clean_work_tree "rebase"
1408+
echo 1 >"$GIT_DIR"/rebase-merge/use-builtin
13061409
exec git rebase--helper ${force_rebase:+--no-ff} --continue
1410+
elif test cross-validate = "$GIT_USE_REBASE_HELPER"
1411+
then
1412+
rm -rf "$GIT_DIR"/saved-rebase-merge
1413+
if test -z "$rebase_root" && test ! -d "$rewritten"
1414+
then
1415+
cp -R "$GIT_DIR"/rebase-merge "$GIT_DIR"/saved-rebase-merge
1416+
git rev-parse HEAD >"$GIT_DIR"/saved-rebase-merge/start-head
1417+
fi
13071418
fi
13081419
do_rest
13091420

0 commit comments

Comments
 (0)