Skip to content

Commit 48af1fd

Browse files
rscharfegitster
authored andcommitted
bisect--helper: double-check run command on exit code 126 and 127
When a run command cannot be executed or found, shells return exit code 126 or 127, respectively. Valid run commands are allowed to return these codes as well to indicate bad revisions, though, for historical reasons. This means typos can cause bogus bisect runs that go over the full distance and end up reporting invalid results. The best solution would be to reserve exit codes 126 and 127, like 71b0251 (Bisect run: "skip" current commit if script exit code is 125., 2007-10-26) did for 125, and abort bisect run when we get them. That might be inconvenient for those who relied on the documentation stating that 126 and 127 can be used for bad revisions, though. The workaround used by this patch is to run the command on a known-good revision and abort if we still get the same error code. This adds one step to runs with scripts that use exit codes 126 and 127, but keeps them supported, with one exception: It won't work with commands that cannot recognize the (manually marked) known-good revision as such. Run commands that use low exit codes are unaffected. Typos are reported after executing the missing command twice and three checkouts (the first step, the known good revision and back to the revision of the first step). Signed-off-by: René Scharfe <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ba5bb81 commit 48af1fd

File tree

4 files changed

+71
-3
lines changed

4 files changed

+71
-3
lines changed

bisect.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,8 @@ static int is_expected_rev(const struct object_id *oid)
724724
return res;
725725
}
726726

727-
static enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
727+
enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
728+
int no_checkout)
728729
{
729730
char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
730731
struct commit *commit;

bisect.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
struct commit_list;
55
struct repository;
6+
struct object_id;
67

78
/*
89
* Find bisection. If something is found, `reaches` will be the number of
@@ -69,4 +70,7 @@ void read_bisect_terms(const char **bad, const char **good);
6970

7071
int bisect_clean_state(void);
7172

73+
enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
74+
int no_checkout);
75+
7276
#endif

builtin/bisect--helper.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,13 +1089,52 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a
10891089
return res;
10901090
}
10911091

1092+
static int get_first_good(const char *refname, const struct object_id *oid,
1093+
int flag, void *cb_data)
1094+
{
1095+
oidcpy(cb_data, oid);
1096+
return 1;
1097+
}
1098+
1099+
static int verify_good(const struct bisect_terms *terms,
1100+
const char **quoted_argv)
1101+
{
1102+
int rc;
1103+
enum bisect_error res;
1104+
struct object_id good_rev;
1105+
struct object_id current_rev;
1106+
char *good_glob = xstrfmt("%s-*", terms->term_good);
1107+
int no_checkout = ref_exists("BISECT_HEAD");
1108+
1109+
for_each_glob_ref_in(get_first_good, good_glob, "refs/bisect/",
1110+
&good_rev);
1111+
free(good_glob);
1112+
1113+
if (read_ref(no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev))
1114+
return -1;
1115+
1116+
res = bisect_checkout(&good_rev, no_checkout);
1117+
if (res != BISECT_OK)
1118+
return -1;
1119+
1120+
printf(_("running %s\n"), quoted_argv[0]);
1121+
rc = run_command_v_opt(quoted_argv, RUN_USING_SHELL);
1122+
1123+
res = bisect_checkout(&current_rev, no_checkout);
1124+
if (res != BISECT_OK)
1125+
return -1;
1126+
1127+
return rc;
1128+
}
1129+
10921130
static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
10931131
{
10941132
int res = BISECT_OK;
10951133
struct strbuf command = STRBUF_INIT;
10961134
struct strvec run_args = STRVEC_INIT;
10971135
const char *new_state;
10981136
int temporary_stdout_fd, saved_stdout;
1137+
int is_first_run = 1;
10991138

11001139
if (bisect_next_check(terms, NULL))
11011140
return BISECT_FAILED;
@@ -1113,6 +1152,30 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
11131152
printf(_("running %s\n"), command.buf);
11141153
res = run_command_v_opt(run_args.v, RUN_USING_SHELL);
11151154

1155+
/*
1156+
* Exit code 126 and 127 can either come from the shell
1157+
* if it was unable to execute or even find the script,
1158+
* or from the script itself. Check with a known-good
1159+
* revision to avoid trashing the bisect run due to a
1160+
* missing or non-executable script.
1161+
*/
1162+
if (is_first_run && (res == 126 || res == 127)) {
1163+
int rc = verify_good(terms, run_args.v);
1164+
is_first_run = 0;
1165+
if (rc < 0) {
1166+
error(_("unable to verify '%s' on good"
1167+
" revision"), command.buf);
1168+
res = BISECT_FAILED;
1169+
break;
1170+
}
1171+
if (rc == res) {
1172+
error(_("bogus exit code %d for good revision"),
1173+
rc);
1174+
res = BISECT_FAILED;
1175+
break;
1176+
}
1177+
}
1178+
11161179
if (res < 0 || 128 <= res) {
11171180
error(_("bisect run failed: exit code %d from"
11181181
" '%s' is < 0 or >= 128"), res, command.buf);

t/t6030-bisect-porcelain.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ test_expect_success 'bisect run accepts exit code 126 as bad' '
290290
grep "$HASH3 is the first bad commit" my_bisect_log.txt
291291
'
292292

293-
test_expect_failure POSIXPERM 'bisect run fails with non-executable test script' '
293+
test_expect_success POSIXPERM 'bisect run fails with non-executable test script' '
294294
test_when_finished "git bisect reset" &&
295295
>not-executable.sh &&
296296
chmod -x not-executable.sh &&
@@ -313,7 +313,7 @@ test_expect_success 'bisect run accepts exit code 127 as bad' '
313313
grep "$HASH3 is the first bad commit" my_bisect_log.txt
314314
'
315315

316-
test_expect_failure 'bisect run fails with missing test script' '
316+
test_expect_success 'bisect run fails with missing test script' '
317317
test_when_finished "git bisect reset" &&
318318
rm -f does-not-exist.sh &&
319319
git bisect start &&

0 commit comments

Comments
 (0)