Skip to content

Commit bb603c9

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 <johannes.schindelin@gmx.de>
1 parent 5ea721e commit bb603c9

File tree

4 files changed

+192
-55
lines changed

4 files changed

+192
-55
lines changed

git-rebase--interactive.sh

Lines changed: 130 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.
@@ -676,6 +679,30 @@ do_next () {
676679
fi &&
677680
warn "Successfully rebased and updated $head_name."
678681

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

@@ -743,11 +770,27 @@ transform_todo_ids () {
743770
}
744771

745772
expand_todo_ids() {
773+
cp "$todo" "$todo".transform
774+
transform_todo_ids
775+
mv "$todo" "$todo".transformed
776+
cp "$todo".transform "$todo"
746777
git rebase--helper --expand-sha1s
778+
if ! git diff --no-index -w -- "$todo" "$todo.transformed"
779+
then
780+
die "rebase--helper failed to expand todo IDs"
781+
fi
747782
}
748783

749784
collapse_todo_ids() {
785+
cp "$todo" "$todo".transform
786+
transform_todo_ids --short
787+
mv "$todo" "$todo".transformed
788+
cp "$todo".transform "$todo"
750789
git rebase--helper --shorten-sha1s
790+
if ! git diff --no-index -w -- "$todo" "$todo.transformed"
791+
then
792+
die "rebase--helper failed to collapse todo IDs"
793+
fi
751794
}
752795

753796
# Rearrange the todo list that has both "pick sha1 msg" and
@@ -1032,6 +1075,23 @@ check_todo_list () {
10321075
fi
10331076
}
10341077

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

10461106
case "$action" in
10471107
continue)
1048-
if test ! -d "$rewritten"
1108+
if test -f "$GIT_DIR"/saved-rebase-merge/cross-validating
1109+
then
1110+
git rebase--helper ${force_rebase:+--no-ff} --continue &&
1111+
if test ! -f "$todo"
1112+
then
1113+
check_rebase__helper
1114+
fi
1115+
return
1116+
fi
1117+
1118+
if test -f "$GIT_DIR"/rebase-merge/use-builtin
10491119
then
10501120
exec git rebase--helper ${force_rebase:+--no-ff} --continue
10511121
fi
1122+
10521123
# do we have anything to commit?
10531124
if git diff-index --cached --quiet HEAD --
10541125
then
@@ -1106,10 +1177,21 @@ first and then run 'git rebase --continue' again."
11061177
skip)
11071178
git rerere clear
11081179

1109-
if test ! -d "$rewritten"
1180+
if test -f "$GIT_DIR"/saved-rebase-merge/cross-validating
1181+
then
1182+
git rebase--helper ${force_rebase:+--no-ff} --continue &&
1183+
if test ! -f "$todo"
1184+
then
1185+
check_rebase__helper
1186+
fi
1187+
return
1188+
fi
1189+
1190+
if test -f "$GIT_DIR"/rebase-merge/use-builtin
11101191
then
11111192
exec git rebase--helper ${force_rebase:+--no-ff} --continue
11121193
fi
1194+
11131195
do_rest
11141196
return 0
11151197
;;
@@ -1188,27 +1270,25 @@ else
11881270
revisions=$onto...$orig_head
11891271
shortrevisions=$shorthead
11901272
fi
1191-
if test t != "$preserve_merges"
1192-
then
1193-
git rebase--helper --make-script ${keep_empty:+--keep-empty} \
1194-
$revisions ${restrict_revision+^$restrict_revision} > "$todo"
1195-
else
1196-
format=$(git config --get rebase.instructionFormat)
1197-
# the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
1198-
git rev-list $merges_option --format="%m%H ${format:-%s}" \
1199-
--reverse --left-right --topo-order \
1200-
$revisions ${restrict_revision+^$restrict_revision} | \
1201-
sed -n "s/^>//p" |
1202-
while read -r sha1 rest
1203-
do
1204-
1205-
if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
1206-
then
1207-
comment_out="$comment_char "
1208-
else
1209-
comment_out=
1210-
fi
1273+
format=$(git config --get rebase.instructionFormat)
1274+
# the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
1275+
git rev-list $merges_option --format="%m%H ${format:-%s}" \
1276+
--reverse --left-right --topo-order \
1277+
$revisions ${restrict_revision+^$restrict_revision} | \
1278+
sed -n "s/^>//p" |
1279+
while read -r sha1 rest
1280+
do
1281+
if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
1282+
then
1283+
comment_out="$comment_char "
1284+
else
1285+
comment_out=
1286+
fi
12111287

1288+
if test t != "$preserve_merges"
1289+
then
1290+
printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
1291+
else
12121292
if test -z "$rebase_root"
12131293
then
12141294
preserve=t
@@ -1227,7 +1307,23 @@ else
12271307
touch "$rewritten"/$sha1
12281308
printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
12291309
fi
1230-
done
1310+
fi
1311+
done
1312+
if test -z "$rebase_root" && test ! -d "$rewritten"
1313+
then
1314+
git rebase--helper --make-script ${keep_empty:+--keep-empty} \
1315+
$revisions ${restrict_revision+^$restrict_revision} \
1316+
>"$todo".helped
1317+
if test ! -s "$todo".helped
1318+
then
1319+
test ! -f "$todo" || die "$todo.helped should be empty"
1320+
elif ! cmp "$todo" "$todo".helped
1321+
then
1322+
echo git rebase--helper --make-script \
1323+
${keep_empty:+--keep-empty} $revisions \
1324+
${restrict_revision+^$restrict_revision} >"$todo".gen
1325+
die "make-script generated something incompatible"
1326+
fi
12311327
fi
12321328

12331329
# Watch for commits that been dropped by --cherry-pick
@@ -1299,10 +1395,20 @@ expand_todo_ids
12991395
test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
13001396

13011397
checkout_onto
1302-
if test -z "$rebase_root" && test ! -d "$rewritten"
1398+
if test true = "$GIT_USE_REBASE_HELPER" && test -z "$rebase_root" &&
1399+
test ! -d "$rewritten"
13031400
then
13041401
require_clean_work_tree "rebase"
1402+
echo 1 >"$GIT_DIR"/rebase-merge/use-builtin
13051403
exec git rebase--helper ${force_rebase:+--no-ff} --continue
1404+
elif test cross-validate = "$GIT_USE_REBASE_HELPER"
1405+
then
1406+
rm -rf "$GIT_DIR"/saved-rebase-merge
1407+
if test -z "$rebase_root" && test ! -d "$rewritten"
1408+
then
1409+
cp -R "$GIT_DIR"/rebase-merge "$GIT_DIR"/saved-rebase-merge
1410+
git rev-parse HEAD >"$GIT_DIR"/saved-rebase-merge/start-head
1411+
fi
13061412
fi
13071413
do_rest
13081414

0 commit comments

Comments
 (0)