Skip to content

Commit 0086b9a

Browse files
committed
Merge branch 'mr/bisect-in-c-1' into pu
* mr/bisect-in-c-1: bisect: libify `bisect_next_all` bisect: libify `handle_bad_merge_base` and its dependents bisect: libify `check_good_are_ancestors_of_bad` and its dependents bisect: libify `check_merge_bases` and its dependents bisect: libify `bisect_checkout` bisect: libify `exit_if_skipped_commits` to `error_if_skipped*` and its dependents bisect--helper: return error codes from `cmd_bisect__helper()` bisect: add enum to represent bisect returning codes bisect--helper: introduce new `decide_next()` function run-command: make `exists_in_PATH()` non-static bisect: use the standard 'if (!var)' way to check for 0 bisect--helper: change `retval` to `res` bisect--helper: convert `vocab_*` char pointers to char arrays
2 parents 73a6011 + eb33793 commit 0086b9a

File tree

5 files changed

+201
-99
lines changed

5 files changed

+201
-99
lines changed

bisect.c

Lines changed: 94 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ static int sqrti(int val)
572572
{
573573
float d, x = val;
574574

575-
if (val == 0)
575+
if (!val)
576576
return 0;
577577

578578
do {
@@ -661,11 +661,11 @@ static void bisect_common(struct rev_info *revs)
661661
mark_edges_uninteresting(revs, NULL, 0);
662662
}
663663

664-
static void exit_if_skipped_commits(struct commit_list *tried,
664+
static enum bisect_error error_if_skipped_commits(struct commit_list *tried,
665665
const struct object_id *bad)
666666
{
667667
if (!tried)
668-
return;
668+
return BISECT_OK;
669669

670670
printf("There are only 'skip'ped commits left to test.\n"
671671
"The first %s commit could be any of:\n", term_bad);
@@ -676,7 +676,8 @@ static void exit_if_skipped_commits(struct commit_list *tried,
676676
if (bad)
677677
printf("%s\n", oid_to_hex(bad));
678678
printf(_("We cannot bisect more!\n"));
679-
exit(2);
679+
680+
return BISECT_ONLY_SKIPPED_LEFT;
680681
}
681682

682683
static int is_expected_rev(const struct object_id *oid)
@@ -703,9 +704,10 @@ static int is_expected_rev(const struct object_id *oid)
703704
return res;
704705
}
705706

706-
static int bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
707+
static enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
707708
{
708709
char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
710+
enum bisect_error res = BISECT_OK;
709711

710712
memcpy(bisect_rev_hex, oid_to_hex(bisect_rev), the_hash_algo->hexsz + 1);
711713
update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
@@ -715,14 +717,24 @@ static int bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
715717
update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0,
716718
UPDATE_REFS_DIE_ON_ERR);
717719
} else {
718-
int res;
719720
res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
720721
if (res)
721-
exit(res);
722+
/*
723+
* Errors in `run_command()` itself, signaled by res < 0,
724+
* and errors in the child process, signaled by res > 0
725+
* can both be treated as regular BISECT_FAILURE (-1).
726+
*/
727+
return -abs(res);
722728
}
723729

724730
argv_show_branch[1] = bisect_rev_hex;
725-
return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
731+
res = run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
732+
/*
733+
* Errors in `run_command()` itself, signaled by res < 0,
734+
* and errors in the child process, signaled by res > 0
735+
* can both be treated as regular BISECT_FAILURE (-1).
736+
*/
737+
return -abs(res);
726738
}
727739

728740
static struct commit *get_commit_reference(struct repository *r,
@@ -749,7 +761,7 @@ static struct commit **get_bad_and_good_commits(struct repository *r,
749761
return rev;
750762
}
751763

752-
static void handle_bad_merge_base(void)
764+
static enum bisect_error handle_bad_merge_base(void)
753765
{
754766
if (is_expected_rev(current_bad_oid)) {
755767
char *bad_hex = oid_to_hex(current_bad_oid);
@@ -770,14 +782,14 @@ static void handle_bad_merge_base(void)
770782
"between %s and [%s].\n"),
771783
bad_hex, term_bad, term_good, bad_hex, good_hex);
772784
}
773-
exit(3);
785+
return BISECT_MERGE_BASE_CHECK;
774786
}
775787

776788
fprintf(stderr, _("Some %s revs are not ancestors of the %s rev.\n"
777789
"git bisect cannot work properly in this case.\n"
778790
"Maybe you mistook %s and %s revs?\n"),
779791
term_good, term_bad, term_good, term_bad);
780-
exit(1);
792+
return BISECT_FAILED;
781793
}
782794

783795
static void handle_skipped_merge_base(const struct object_id *mb)
@@ -799,32 +811,43 @@ static void handle_skipped_merge_base(const struct object_id *mb)
799811
* "check_merge_bases" checks that merge bases are not "bad" (or "new").
800812
*
801813
* - If one is "bad" (or "new"), it means the user assumed something wrong
802-
* and we must exit with a non 0 error code.
814+
* and we must return error with a non 0 error code.
803815
* - If one is "good" (or "old"), that's good, we have nothing to do.
804816
* - If one is "skipped", we can't know but we should warn.
805817
* - If we don't know, we should check it out and ask the user to test.
818+
* - If a merge base must be tested, on success return
819+
* BISECT_INTERNAL_SUCCESS_MERGE_BASE (-11) a special condition
820+
* for early success, this will be converted back to 0 in
821+
* check_good_are_ancestors_of_bad().
806822
*/
807-
static void check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
823+
static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
808824
{
825+
enum bisect_error res = BISECT_OK;
809826
struct commit_list *result;
810827

811828
result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
812829

813830
for (; result; result = result->next) {
814831
const struct object_id *mb = &result->item->object.oid;
815832
if (oideq(mb, current_bad_oid)) {
816-
handle_bad_merge_base();
833+
res = handle_bad_merge_base();
834+
break;
817835
} else if (0 <= oid_array_lookup(&good_revs, mb)) {
818836
continue;
819837
} else if (0 <= oid_array_lookup(&skipped_revs, mb)) {
820838
handle_skipped_merge_base(mb);
821839
} else {
822840
printf(_("Bisecting: a merge base must be tested\n"));
823-
exit(bisect_checkout(mb, no_checkout));
841+
res = bisect_checkout(mb, no_checkout);
842+
if (!res)
843+
/* indicate early success */
844+
res = BISECT_INTERNAL_SUCCESS_MERGE_BASE;
845+
break;
824846
}
825847
}
826848

827849
free_commit_list(result);
850+
return res;
828851
}
829852

830853
static int check_ancestors(struct repository *r, int rev_nr,
@@ -850,43 +873,58 @@ static int check_ancestors(struct repository *r, int rev_nr,
850873
*
851874
* If that's not the case, we need to check the merge bases.
852875
* If a merge base must be tested by the user, its source code will be
853-
* checked out to be tested by the user and we will exit.
876+
* checked out to be tested by the user and we will return.
854877
*/
855-
static void check_good_are_ancestors_of_bad(struct repository *r,
878+
879+
static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,
856880
const char *prefix,
857881
int no_checkout)
858882
{
859-
char *filename = git_pathdup("BISECT_ANCESTORS_OK");
883+
char *filename;
860884
struct stat st;
861885
int fd, rev_nr;
886+
enum bisect_error res = BISECT_OK;
862887
struct commit **rev;
863888

864889
if (!current_bad_oid)
865-
die(_("a %s revision is needed"), term_bad);
890+
return error(_("a %s revision is needed"), term_bad);
891+
892+
filename = git_pathdup("BISECT_ANCESTORS_OK");
866893

867894
/* Check if file BISECT_ANCESTORS_OK exists. */
868895
if (!stat(filename, &st) && S_ISREG(st.st_mode))
869896
goto done;
870897

871898
/* Bisecting with no good rev is ok. */
872-
if (good_revs.nr == 0)
899+
if (!good_revs.nr)
873900
goto done;
874901

875902
/* Check if all good revs are ancestor of the bad rev. */
903+
876904
rev = get_bad_and_good_commits(r, &rev_nr);
877905
if (check_ancestors(r, rev_nr, rev, prefix))
878-
check_merge_bases(rev_nr, rev, no_checkout);
906+
res = check_merge_bases(rev_nr, rev, no_checkout);
879907
free(rev);
880908

881-
/* Create file BISECT_ANCESTORS_OK. */
882-
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
883-
if (fd < 0)
884-
warning_errno(_("could not create file '%s'"),
885-
filename);
886-
else
887-
close(fd);
909+
if (!res) {
910+
/* Create file BISECT_ANCESTORS_OK. */
911+
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
912+
if (fd < 0)
913+
/*
914+
* BISECT_ANCESTORS_OK file is not absolutely necessary,
915+
* the bisection process will continue at the next
916+
* bisection step.
917+
* So, just signal with a warning that something
918+
* might be wrong.
919+
*/
920+
warning_errno(_("could not create file '%s'"),
921+
filename);
922+
else
923+
close(fd);
924+
}
888925
done:
889926
free(filename);
927+
return res;
890928
}
891929

892930
/*
@@ -938,26 +976,29 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
938976
}
939977

940978
/*
941-
* We use the convention that exiting with an exit code 10 means that
979+
* We use the convention that return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND (-10) means
942980
* the bisection process finished successfully.
943-
* In this case the calling shell script should exit 0.
944-
*
981+
* In this case the calling function or command should not turn a
982+
* BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND return code into an error or a non zero exit code.
945983
* If no_checkout is non-zero, the bisection process does not
946984
* checkout the trial commit but instead simply updates BISECT_HEAD.
947985
*/
948-
int bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
986+
enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
949987
{
950988
struct rev_info revs;
951989
struct commit_list *tried;
952990
int reaches = 0, all = 0, nr, steps;
991+
enum bisect_error res = BISECT_OK;
953992
struct object_id *bisect_rev;
954993
char *steps_msg;
955994

956995
read_bisect_terms(&term_bad, &term_good);
957996
if (read_bisect_refs())
958997
die(_("reading bisect refs failed"));
959998

960-
check_good_are_ancestors_of_bad(r, prefix, no_checkout);
999+
res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
1000+
if (res)
1001+
return res;
9611002

9621003
bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
9631004
revs.limited = 1;
@@ -969,33 +1010,45 @@ int bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
9691010

9701011
if (!revs.commits) {
9711012
/*
972-
* We should exit here only if the "bad"
1013+
* We should return error here only if the "bad"
9731014
* commit is also a "skip" commit.
9741015
*/
975-
exit_if_skipped_commits(tried, NULL);
976-
1016+
res = error_if_skipped_commits(tried, NULL);
1017+
if (res < 0)
1018+
return res;
9771019
printf(_("%s was both %s and %s\n"),
9781020
oid_to_hex(current_bad_oid),
9791021
term_good,
9801022
term_bad);
981-
exit(1);
1023+
1024+
return BISECT_FAILED;
9821025
}
9831026

9841027
if (!all) {
9851028
fprintf(stderr, _("No testable commit found.\n"
9861029
"Maybe you started with bad path parameters?\n"));
987-
exit(4);
1030+
1031+
return BISECT_NO_TESTABLE_COMMIT;
9881032
}
9891033

9901034
bisect_rev = &revs.commits->item->object.oid;
9911035

9921036
if (oideq(bisect_rev, current_bad_oid)) {
993-
exit_if_skipped_commits(tried, current_bad_oid);
1037+
res = error_if_skipped_commits(tried, current_bad_oid);
1038+
if (res)
1039+
return res;
9941040
printf("%s is the first %s commit\n", oid_to_hex(bisect_rev),
9951041
term_bad);
1042+
9961043
show_diff_tree(r, prefix, revs.commits->item);
997-
/* This means the bisection process succeeded. */
998-
exit(10);
1044+
/*
1045+
* This means the bisection process succeeded.
1046+
* Using BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND (-10)
1047+
* so that the call chain can simply check
1048+
* for negative return values for early returns up
1049+
* until the cmd_bisect__helper() caller.
1050+
*/
1051+
return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
9991052
}
10001053

10011054
nr = all - reaches - 1;

bisect.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,34 @@ struct rev_list_info {
3131
const char *header_prefix;
3232
};
3333

34-
int bisect_next_all(struct repository *r,
34+
/*
35+
* enum bisect_error represents the following return codes:
36+
* BISECT_OK: success code. Internally, it means that next
37+
* commit has been found (and possibly checked out) and it
38+
* should be tested.
39+
* BISECT_FAILED error code: default error code.
40+
* BISECT_ONLY_SKIPPED_LEFT error code: only skipped
41+
* commits left to be tested.
42+
* BISECT_MERGE_BASE_CHECK error code: merge base check failed.
43+
* BISECT_NO_TESTABLE_COMMIT error code: no testable commit found.
44+
* BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND early success code:
45+
* first term_bad commit found.
46+
* BISECT_INTERNAL_SUCCESS_MERGE_BASE early success
47+
* code: found merge base that should be tested.
48+
* Early success codes BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND and
49+
* BISECT_INTERNAL_SUCCESS_MERGE_BASE should be only internal codes.
50+
*/
51+
enum bisect_error {
52+
BISECT_OK = 0,
53+
BISECT_FAILED = -1,
54+
BISECT_ONLY_SKIPPED_LEFT = -2,
55+
BISECT_MERGE_BASE_CHECK = -3,
56+
BISECT_NO_TESTABLE_COMMIT = -4,
57+
BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND = -10,
58+
BISECT_INTERNAL_SUCCESS_MERGE_BASE = -11
59+
};
60+
61+
enum bisect_error bisect_next_all(struct repository *r,
3562
const char *prefix,
3663
int no_checkout);
3764

0 commit comments

Comments
 (0)