Skip to content

Commit 4e3f7ba

Browse files
committed
Merge branch 'pb/bisect' into pu
Move more parts of "git bisect" to C. * pb/bisect: (27 commits) bisect--helper: remove the dequote in bisect_start() bisect--helper: retire `--bisect-auto-next` subcommand bisect--helper: retire `--bisect-autostart` subcommand bisect--helper: retire `--bisect-write` subcommand bisect--helper: `bisect_replay` shell function in C bisect--helper: `bisect_log` shell function in C bisect--helper: retire `--write-terms` subcommand bisect--helper: retire `--check-expected-revs` subcommand bisect--helper: `bisect_state` & `bisect_head` shell function in C bisect--helper: `bisect_autostart` shell function in C bisect--helper: retire `--next-all` subcommand bisect--helper: retire `--bisect-clean-state` subcommand bisect--helper: `bisect_next` and `bisect_auto_next` shell function in C t6030: no cleanup with bad merge base bisect--helper: `bisect_start` shell function partially in C bisect--helper: `get_terms` & `bisect_terms` shell function in C bisect--helper: `bisect_next_check` & bisect_voc shell function in C bisect--helper: `check_and_set_terms` shell function in C bisect--helper: `bisect_write` shell function in C bisect--helper: `is_expected_rev` & `check_expected_revs` shell function in C ...
2 parents 0e4c4f8 + 9889c96 commit 4e3f7ba

File tree

8 files changed

+1257
-584
lines changed

8 files changed

+1257
-584
lines changed

bisect.c

Lines changed: 142 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,12 @@ static int read_bisect_refs(void)
430430

431431
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
432432
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
433+
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
434+
static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
435+
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
436+
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
437+
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
438+
static GIT_PATH_FUNC(git_path_head_name, "head-name")
433439

434440
static void read_bisect_paths(struct argv_array *array)
435441
{
@@ -612,6 +618,12 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
612618
struct argv_array rev_argv = ARGV_ARRAY_INIT;
613619
int i;
614620

621+
/*
622+
* Since the code is slowly being converted to C, there might be
623+
* instances where the revisions were initialized before. Thus
624+
* we first need to reset it.
625+
*/
626+
reset_revision_walk();
615627
init_revisions(revs, prefix);
616628
revs->abbrev = 0;
617629
revs->commit_format = CMIT_FMT_UNSPECIFIED;
@@ -632,17 +644,22 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
632644

633645
static void bisect_common(struct rev_info *revs)
634646
{
647+
/*
648+
* We don't want to clean the bisection state
649+
* as we need to get back to where we started
650+
* by using `git bisect reset`.
651+
*/
635652
if (prepare_revision_walk(revs))
636653
die("revision walk setup failed");
637654
if (revs->tree_objects)
638655
mark_edges_uninteresting(revs, NULL);
639656
}
640657

641-
static void exit_if_skipped_commits(struct commit_list *tried,
658+
static int exit_if_skipped_commits(struct commit_list *tried,
642659
const struct object_id *bad)
643660
{
644661
if (!tried)
645-
return;
662+
return 0;
646663

647664
printf("There are only 'skip'ped commits left to test.\n"
648665
"The first %s commit could be any of:\n", term_bad);
@@ -653,7 +670,13 @@ static void exit_if_skipped_commits(struct commit_list *tried,
653670
if (bad)
654671
printf("%s\n", oid_to_hex(bad));
655672
printf(_("We cannot bisect more!\n"));
656-
exit(2);
673+
674+
/*
675+
* We don't want to clean the bisection state
676+
* as we need to get back to where we started
677+
* by using `git bisect reset`.
678+
*/
679+
return 2;
657680
}
658681

659682
static int is_expected_rev(const struct object_id *oid)
@@ -694,7 +717,7 @@ static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
694717
int res;
695718
res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
696719
if (res)
697-
exit(res);
720+
return res;
698721
}
699722

700723
argv_show_branch[1] = bisect_rev_hex;
@@ -723,7 +746,7 @@ static struct commit **get_bad_and_good_commits(int *rev_nr)
723746
return rev;
724747
}
725748

726-
static void handle_bad_merge_base(void)
749+
static int handle_bad_merge_base(void)
727750
{
728751
if (is_expected_rev(current_bad_oid)) {
729752
char *bad_hex = oid_to_hex(current_bad_oid);
@@ -744,17 +767,23 @@ static void handle_bad_merge_base(void)
744767
"between %s and [%s].\n"),
745768
bad_hex, term_bad, term_good, bad_hex, good_hex);
746769
}
747-
exit(3);
770+
/*
771+
* We don't want to clean the bisection state
772+
* as we need to get back to where we started
773+
* by using `git bisect reset`.
774+
*/
775+
return 3;
748776
}
749777

750778
fprintf(stderr, _("Some %s revs are not ancestors of the %s rev.\n"
751779
"git bisect cannot work properly in this case.\n"
752780
"Maybe you mistook %s and %s revs?\n"),
753781
term_good, term_bad, term_good, term_bad);
754-
exit(1);
782+
bisect_clean_state();
783+
return 1;
755784
}
756785

757-
static void handle_skipped_merge_base(const unsigned char *mb)
786+
static int handle_skipped_merge_base(const unsigned char *mb)
758787
{
759788
char *mb_hex = sha1_to_hex(mb);
760789
char *bad_hex = oid_to_hex(current_bad_oid);
@@ -767,41 +796,53 @@ static void handle_skipped_merge_base(const unsigned char *mb)
767796
"We continue anyway."),
768797
bad_hex, good_hex, term_bad, mb_hex, bad_hex);
769798
free(good_hex);
799+
return 0;
770800
}
771801

772802
/*
773803
* "check_merge_bases" checks that merge bases are not "bad" (or "new").
774804
*
775805
* - If one is "bad" (or "new"), it means the user assumed something wrong
776-
* and we must exit with a non 0 error code.
806+
* and we must return error with a non 0 error code.
777807
* - If one is "good" (or "old"), that's good, we have nothing to do.
778808
* - If one is "skipped", we can't know but we should warn.
779809
* - If we don't know, we should check it out and ask the user to test.
780810
*/
781-
static void check_merge_bases(int no_checkout)
811+
static int check_merge_bases(int no_checkout)
782812
{
783813
struct commit_list *result;
784-
int rev_nr;
814+
int rev_nr, res = 0;
785815
struct commit **rev = get_bad_and_good_commits(&rev_nr);
786816

787817
result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
788818

789819
for (; result; result = result->next) {
790820
const unsigned char *mb = result->item->object.oid.hash;
791821
if (!hashcmp(mb, current_bad_oid->hash)) {
792-
handle_bad_merge_base();
822+
res = handle_bad_merge_base();
823+
break;
793824
} else if (0 <= sha1_array_lookup(&good_revs, mb)) {
794825
continue;
795826
} else if (0 <= sha1_array_lookup(&skipped_revs, mb)) {
796-
handle_skipped_merge_base(mb);
827+
res = handle_skipped_merge_base(mb);
828+
break;
797829
} else {
798830
printf(_("Bisecting: a merge base must be tested\n"));
799-
exit(bisect_checkout(mb, no_checkout));
831+
res = bisect_checkout(mb, no_checkout);
832+
/*
833+
* We don't want to clean the bisection state
834+
* as we need to get back to where we started
835+
* by using `git bisect reset`.
836+
*/
837+
if (!res)
838+
exit(0);
839+
break;
800840
}
801841
}
802842

803843
free(rev);
804844
free_commit_list(result);
845+
return res;
805846
}
806847

807848
static int check_ancestors(const char *prefix)
@@ -837,16 +878,21 @@ static int check_ancestors(const char *prefix)
837878
*
838879
* If that's not the case, we need to check the merge bases.
839880
* If a merge base must be tested by the user, its source code will be
840-
* checked out to be tested by the user and we will exit.
881+
* checked out to be tested by the user and we will return.
841882
*/
842-
static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
883+
static int check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
843884
{
844885
char *filename = git_pathdup("BISECT_ANCESTORS_OK");
845886
struct stat st;
846-
int fd;
887+
int fd, res = 0;
847888

889+
/*
890+
* We don't want to clean the bisection state
891+
* as we need to get back to where we started
892+
* by using `git bisect reset`.
893+
*/
848894
if (!current_bad_oid)
849-
die(_("a %s revision is needed"), term_bad);
895+
error(_("a %s revision is needed"), term_bad);
850896

851897
/* Check if file BISECT_ANCESTORS_OK exists. */
852898
if (!stat(filename, &st) && S_ISREG(st.st_mode))
@@ -858,7 +904,10 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
858904

859905
/* Check if all good revs are ancestor of the bad rev. */
860906
if (check_ancestors(prefix))
861-
check_merge_bases(no_checkout);
907+
res = check_merge_bases(no_checkout);
908+
909+
if (res)
910+
goto done;
862911

863912
/* Create file BISECT_ANCESTORS_OK. */
864913
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
@@ -867,8 +916,11 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
867916
filename);
868917
else
869918
close(fd);
919+
920+
goto done;
870921
done:
871922
free(filename);
923+
return res;
872924
}
873925

874926
/*
@@ -927,7 +979,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
927979
}
928980

929981
/*
930-
* We use the convention that exiting with an exit code 10 means that
982+
* We use the convention to return with an return code 10 means that
931983
* the bisection process finished successfully.
932984
* In this case the calling shell script should exit 0.
933985
*
@@ -938,15 +990,17 @@ int bisect_next_all(const char *prefix, int no_checkout)
938990
{
939991
struct rev_info revs;
940992
struct commit_list *tried;
941-
int reaches = 0, all = 0, nr, steps;
993+
int reaches = 0, all = 0, nr, steps, res;
942994
const unsigned char *bisect_rev;
943995
char steps_msg[32];
944996

945997
read_bisect_terms(&term_bad, &term_good);
946998
if (read_bisect_refs())
947999
die(_("reading bisect refs failed"));
9481000

949-
check_good_are_ancestors_of_bad(prefix, no_checkout);
1001+
res = check_good_are_ancestors_of_bad(prefix, no_checkout);
1002+
if (res)
1003+
return res;
9501004

9511005
bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
9521006
revs.limited = 1;
@@ -958,34 +1012,52 @@ int bisect_next_all(const char *prefix, int no_checkout)
9581012
revs.commits = managed_skipped(revs.commits, &tried);
9591013

9601014
if (!revs.commits) {
1015+
int res;
9611016
/*
962-
* We should exit here only if the "bad"
1017+
* We should return error here only if the "bad"
9631018
* commit is also a "skip" commit.
9641019
*/
965-
exit_if_skipped_commits(tried, NULL);
1020+
res = exit_if_skipped_commits(tried, NULL);
1021+
if (res)
1022+
return res;
9661023

9671024
printf(_("%s was both %s and %s\n"),
9681025
oid_to_hex(current_bad_oid),
9691026
term_good,
9701027
term_bad);
971-
exit(1);
1028+
1029+
/*
1030+
* We don't want to clean the bisection state
1031+
* as we need to get back to where we started
1032+
* by using `git bisect reset`.
1033+
*/
1034+
return 1;
9721035
}
9731036

9741037
if (!all) {
9751038
fprintf(stderr, _("No testable commit found.\n"
9761039
"Maybe you started with bad path parameters?\n"));
977-
exit(4);
1040+
1041+
/*
1042+
* We don't want to clean the bisection state
1043+
* as we need to get back to where we started
1044+
* by using `git bisect reset`.
1045+
*/
1046+
return 4;
9781047
}
9791048

9801049
bisect_rev = revs.commits->item->object.oid.hash;
9811050

9821051
if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
983-
exit_if_skipped_commits(tried, current_bad_oid);
1052+
res = exit_if_skipped_commits(tried, current_bad_oid);
1053+
if (res)
1054+
return res;
1055+
9841056
printf("%s is the first %s commit\n", sha1_to_hex(bisect_rev),
9851057
term_bad);
9861058
show_diff_tree(prefix, revs.commits->item);
9871059
/* This means the bisection process succeeded. */
988-
exit(10);
1060+
return 10;
9891061
}
9901062

9911063
nr = all - reaches - 1;
@@ -999,7 +1071,11 @@ int bisect_next_all(const char *prefix, int no_checkout)
9991071
"Bisecting: %d revisions left to test after this %s\n",
10001072
nr), nr, steps_msg);
10011073

1002-
return bisect_checkout(bisect_rev, no_checkout);
1074+
res = bisect_checkout(bisect_rev, no_checkout);
1075+
if (res)
1076+
bisect_clean_state();
1077+
1078+
return res;
10031079
}
10041080

10051081
static inline int log2i(int n)
@@ -1040,3 +1116,40 @@ int estimate_bisect_steps(int all)
10401116

10411117
return (e < 3 * x) ? n : n - 1;
10421118
}
1119+
1120+
static int mark_for_removal(const char *refname, const struct object_id *oid,
1121+
int flag, void *cb_data)
1122+
{
1123+
struct string_list *refs = cb_data;
1124+
char *ref = xstrfmt("refs/bisect%s", refname);
1125+
string_list_append(refs, ref);
1126+
return 0;
1127+
}
1128+
1129+
int bisect_clean_state(void)
1130+
{
1131+
int result = 0;
1132+
1133+
/* There may be some refs packed during bisection */
1134+
struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
1135+
for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
1136+
string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
1137+
result = delete_refs(&refs_for_removal, REF_NODEREF);
1138+
refs_for_removal.strdup_strings = 1;
1139+
string_list_clear(&refs_for_removal, 0);
1140+
unlink_or_warn(git_path_bisect_expected_rev());
1141+
unlink_or_warn(git_path_bisect_ancestors_ok());
1142+
unlink_or_warn(git_path_bisect_log());
1143+
unlink_or_warn(git_path_bisect_names());
1144+
unlink_or_warn(git_path_bisect_run());
1145+
unlink_or_warn(git_path_bisect_terms());
1146+
/* Cleanup head-name if it got left by an old version of git-bisect */
1147+
unlink_or_warn(git_path_head_name());
1148+
/*
1149+
* Cleanup BISECT_START last to support the --no-checkout option
1150+
* introduced in the commit 4796e823a.
1151+
*/
1152+
unlink_or_warn(git_path_bisect_start());
1153+
1154+
return result;
1155+
}

bisect.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ extern int estimate_bisect_steps(int all);
2828

2929
extern void read_bisect_terms(const char **bad, const char **good);
3030

31+
extern int bisect_clean_state(void);
32+
3133
#endif

0 commit comments

Comments
 (0)