bisect--helper: reimplement bisect_run shell function in C
				
					
				
			Reimplement the `bisect_run()` shell function in C and also add `--bisect-run` subcommand to `git bisect--helper` to call it from git-bisect.sh. Mentored-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Tanushree Tumane <tanushreetumane@gmail.com> Signed-off-by: Miriam Rubio <mirucam@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
		 Tanushree Tumane
					Tanushree Tumane
				
			
				
					committed by
					
						 Junio C Hamano
						Junio C Hamano
					
				
			
			
				
	
			
			
			 Junio C Hamano
						Junio C Hamano
					
				
			
						parent
						
							5e1f28d206
						
					
				
				
					commit
					d1bbbe45df
				
			| @ -18,6 +18,7 @@ static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") | ||||
| static GIT_PATH_FUNC(git_path_head_name, "head-name") | ||||
| static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES") | ||||
| static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT") | ||||
| static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") | ||||
|  | ||||
| static const char * const git_bisect_helper_usage[] = { | ||||
| 	N_("git bisect--helper --bisect-reset [<commit>]"), | ||||
| @ -31,6 +32,7 @@ static const char * const git_bisect_helper_usage[] = { | ||||
| 	N_("git bisect--helper --bisect-replay <filename>"), | ||||
| 	N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"), | ||||
| 	N_("git bisect--helper --bisect-visualize"), | ||||
| 	N_("git bisect--helper --bisect-run <cmd>..."), | ||||
| 	NULL | ||||
| }; | ||||
|  | ||||
| @ -144,6 +146,19 @@ static int append_to_file(const char *path, const char *format, ...) | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| static int print_file_to_stdout(const char *path) | ||||
| { | ||||
| 	int fd = open(path, O_RDONLY); | ||||
| 	int ret = 0; | ||||
|  | ||||
| 	if (fd < 0) | ||||
| 		return error_errno(_("cannot open file '%s' for reading"), path); | ||||
| 	if (copy_fd(fd, 1) < 0) | ||||
| 		ret = error_errno(_("failed to read '%s'"), path); | ||||
| 	close(fd); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static int check_term_format(const char *term, const char *orig_term) | ||||
| { | ||||
| 	int res; | ||||
| @ -1075,6 +1090,87 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) | ||||
| { | ||||
| 	int res = BISECT_OK; | ||||
| 	struct strbuf command = STRBUF_INIT; | ||||
| 	struct strvec args = STRVEC_INIT; | ||||
| 	struct strvec run_args = STRVEC_INIT; | ||||
| 	const char *new_state; | ||||
| 	int temporary_stdout_fd, saved_stdout; | ||||
|  | ||||
| 	if (bisect_next_check(terms, NULL)) | ||||
| 		return BISECT_FAILED; | ||||
|  | ||||
| 	if (argc) | ||||
| 		sq_quote_argv(&command, argv); | ||||
| 	else { | ||||
| 		error(_("bisect run failed: no command provided.")); | ||||
| 		return BISECT_FAILED; | ||||
| 	} | ||||
|  | ||||
| 	strvec_push(&run_args, command.buf); | ||||
|  | ||||
| 	while (1) { | ||||
| 		strvec_clear(&args); | ||||
|  | ||||
| 		printf(_("running %s\n"), command.buf); | ||||
| 		res = run_command_v_opt(run_args.v, RUN_USING_SHELL); | ||||
|  | ||||
| 		if (res < 0 || 128 <= res) { | ||||
| 			error(_("bisect run failed: exit code %d from" | ||||
| 				" '%s' is < 0 or >= 128"), res, command.buf); | ||||
| 			strbuf_release(&command); | ||||
| 			return res; | ||||
| 		} | ||||
|  | ||||
| 		if (res == 125) | ||||
| 			new_state = "skip"; | ||||
| 		else if (!res) | ||||
| 			new_state = terms->term_good; | ||||
| 		else | ||||
| 			new_state = terms->term_bad; | ||||
|  | ||||
| 		temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666); | ||||
|  | ||||
| 		if (temporary_stdout_fd < 0) | ||||
| 			return error_errno(_("cannot open file '%s' for writing"), git_path_bisect_run()); | ||||
|  | ||||
| 		fflush(stdout); | ||||
| 		saved_stdout = dup(1); | ||||
| 		dup2(temporary_stdout_fd, 1); | ||||
|  | ||||
| 		res = bisect_state(terms, &new_state, 1); | ||||
|  | ||||
| 		fflush(stdout); | ||||
| 		dup2(saved_stdout, 1); | ||||
| 		close(saved_stdout); | ||||
| 		close(temporary_stdout_fd); | ||||
|  | ||||
| 		print_file_to_stdout(git_path_bisect_run()); | ||||
|  | ||||
| 		if (res == BISECT_ONLY_SKIPPED_LEFT) | ||||
| 			error(_("bisect run cannot continue any more")); | ||||
| 		else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) { | ||||
| 			printf(_("bisect run success")); | ||||
| 			res = BISECT_OK; | ||||
| 		} else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) { | ||||
| 			printf(_("bisect found first bad commit")); | ||||
| 			res = BISECT_OK; | ||||
| 		} else if (res) { | ||||
| 			error(_("bisect run failed:'git bisect--helper --bisect-state" | ||||
| 			" %s' exited with error code %d"), args.v[0], res); | ||||
| 		} else { | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		strbuf_release(&command); | ||||
| 		strvec_clear(&args); | ||||
| 		strvec_clear(&run_args); | ||||
| 		return res; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int cmd_bisect__helper(int argc, const char **argv, const char *prefix) | ||||
| { | ||||
| 	enum { | ||||
| @ -1089,6 +1185,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) | ||||
| 		BISECT_REPLAY, | ||||
| 		BISECT_SKIP, | ||||
| 		BISECT_VISUALIZE, | ||||
| 		BISECT_RUN, | ||||
| 	} cmdmode = 0; | ||||
| 	int res = 0, nolog = 0; | ||||
| 	struct option options[] = { | ||||
| @ -1112,6 +1209,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) | ||||
| 			 N_("skip some commits for checkout"), BISECT_SKIP), | ||||
| 		OPT_CMDMODE(0, "bisect-visualize", &cmdmode, | ||||
| 			 N_("visualize the bisection"), BISECT_VISUALIZE), | ||||
| 		OPT_CMDMODE(0, "bisect-run", &cmdmode, | ||||
| 			 N_("use <cmd>... to automatically bisect."), BISECT_RUN), | ||||
| 		OPT_BOOL(0, "no-log", &nolog, | ||||
| 			 N_("no log for BISECT_WRITE")), | ||||
| 		OPT_END() | ||||
| @ -1177,6 +1276,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) | ||||
| 		get_terms(&terms); | ||||
| 		res = bisect_visualize(&terms, argv, argc); | ||||
| 		break; | ||||
| 	case BISECT_RUN: | ||||
| 		if (!argc) | ||||
| 			return error(_("bisect run failed: no command provided.")); | ||||
| 		get_terms(&terms); | ||||
| 		res = bisect_run(&terms, argv, argc); | ||||
| 		break; | ||||
| 	default: | ||||
| 		BUG("unknown subcommand %d", cmdmode); | ||||
| 	} | ||||
|  | ||||
| @ -39,66 +39,6 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" | ||||
| TERM_BAD=bad | ||||
| TERM_GOOD=good | ||||
|  | ||||
| bisect_run () { | ||||
| 	git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit | ||||
|  | ||||
| 	test -n "$*" || die "$(gettext "bisect run failed: no command provided.")" | ||||
|  | ||||
| 	while true | ||||
| 	do | ||||
| 		command="$@" | ||||
| 		eval_gettextln "running \$command" | ||||
| 		"$@" | ||||
| 		res=$? | ||||
|  | ||||
| 		# Check for really bad run error. | ||||
| 		if [ $res -lt 0 -o $res -ge 128 ] | ||||
| 		then | ||||
| 			eval_gettextln "bisect run failed: | ||||
| exit code \$res from '\$command' is < 0 or >= 128" >&2 | ||||
| 			exit $res | ||||
| 		fi | ||||
|  | ||||
| 		# Find current state depending on run success or failure. | ||||
| 		# A special exit code of 125 means cannot test. | ||||
| 		if [ $res -eq 125 ] | ||||
| 		then | ||||
| 			state='skip' | ||||
| 		elif [ $res -gt 0 ] | ||||
| 		then | ||||
| 			state="$TERM_BAD" | ||||
| 		else | ||||
| 			state="$TERM_GOOD" | ||||
| 		fi | ||||
|  | ||||
| 		git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN" | ||||
| 		res=$? | ||||
|  | ||||
| 		cat "$GIT_DIR/BISECT_RUN" | ||||
|  | ||||
| 		if sane_grep "first $TERM_BAD commit could be any of" "$GIT_DIR/BISECT_RUN" \ | ||||
| 			>/dev/null | ||||
| 		then | ||||
| 			gettextln "bisect run cannot continue any more" >&2 | ||||
| 			exit $res | ||||
| 		fi | ||||
|  | ||||
| 		if [ $res -ne 0 ] | ||||
| 		then | ||||
| 			eval_gettextln "bisect run failed: | ||||
| 'bisect-state \$state' exited with error code \$res" >&2 | ||||
| 			exit $res | ||||
| 		fi | ||||
|  | ||||
| 		if sane_grep "is the first $TERM_BAD commit" "$GIT_DIR/BISECT_RUN" >/dev/null | ||||
| 		then | ||||
| 			gettextln "bisect run success" | ||||
| 			exit 0; | ||||
| 		fi | ||||
|  | ||||
| 	done | ||||
| } | ||||
|  | ||||
| get_terms () { | ||||
| 	if test -s "$GIT_DIR/BISECT_TERMS" | ||||
| 	then | ||||
| @ -137,7 +77,7 @@ case "$#" in | ||||
| 	log) | ||||
| 		git bisect--helper --bisect-log || exit ;; | ||||
| 	run) | ||||
| 		bisect_run "$@" ;; | ||||
| 		git bisect--helper --bisect-run "$@" || exit;; | ||||
| 	terms) | ||||
| 		git bisect--helper --bisect-terms "$@" || exit;; | ||||
| 	*) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user