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_head_name, "head-name") | ||||||
| static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES") | 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_first_parent, "BISECT_FIRST_PARENT") | ||||||
|  | static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") | ||||||
|  |  | ||||||
| static const char * const git_bisect_helper_usage[] = { | static const char * const git_bisect_helper_usage[] = { | ||||||
| 	N_("git bisect--helper --bisect-reset [<commit>]"), | 	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-replay <filename>"), | ||||||
| 	N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"), | 	N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"), | ||||||
| 	N_("git bisect--helper --bisect-visualize"), | 	N_("git bisect--helper --bisect-visualize"), | ||||||
|  | 	N_("git bisect--helper --bisect-run <cmd>..."), | ||||||
| 	NULL | 	NULL | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @ -144,6 +146,19 @@ static int append_to_file(const char *path, const char *format, ...) | |||||||
| 	return res; | 	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) | static int check_term_format(const char *term, const char *orig_term) | ||||||
| { | { | ||||||
| 	int res; | 	int res; | ||||||
| @ -1075,6 +1090,87 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a | |||||||
| 	return res; | 	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) | int cmd_bisect__helper(int argc, const char **argv, const char *prefix) | ||||||
| { | { | ||||||
| 	enum { | 	enum { | ||||||
| @ -1089,6 +1185,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) | |||||||
| 		BISECT_REPLAY, | 		BISECT_REPLAY, | ||||||
| 		BISECT_SKIP, | 		BISECT_SKIP, | ||||||
| 		BISECT_VISUALIZE, | 		BISECT_VISUALIZE, | ||||||
|  | 		BISECT_RUN, | ||||||
| 	} cmdmode = 0; | 	} cmdmode = 0; | ||||||
| 	int res = 0, nolog = 0; | 	int res = 0, nolog = 0; | ||||||
| 	struct option options[] = { | 	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), | 			 N_("skip some commits for checkout"), BISECT_SKIP), | ||||||
| 		OPT_CMDMODE(0, "bisect-visualize", &cmdmode, | 		OPT_CMDMODE(0, "bisect-visualize", &cmdmode, | ||||||
| 			 N_("visualize the bisection"), BISECT_VISUALIZE), | 			 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, | 		OPT_BOOL(0, "no-log", &nolog, | ||||||
| 			 N_("no log for BISECT_WRITE")), | 			 N_("no log for BISECT_WRITE")), | ||||||
| 		OPT_END() | 		OPT_END() | ||||||
| @ -1177,6 +1276,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) | |||||||
| 		get_terms(&terms); | 		get_terms(&terms); | ||||||
| 		res = bisect_visualize(&terms, argv, argc); | 		res = bisect_visualize(&terms, argv, argc); | ||||||
| 		break; | 		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: | 	default: | ||||||
| 		BUG("unknown subcommand %d", cmdmode); | 		BUG("unknown subcommand %d", cmdmode); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -39,66 +39,6 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" | |||||||
| TERM_BAD=bad | TERM_BAD=bad | ||||||
| TERM_GOOD=good | 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 () { | get_terms () { | ||||||
| 	if test -s "$GIT_DIR/BISECT_TERMS" | 	if test -s "$GIT_DIR/BISECT_TERMS" | ||||||
| 	then | 	then | ||||||
| @ -137,7 +77,7 @@ case "$#" in | |||||||
| 	log) | 	log) | ||||||
| 		git bisect--helper --bisect-log || exit ;; | 		git bisect--helper --bisect-log || exit ;; | ||||||
| 	run) | 	run) | ||||||
| 		bisect_run "$@" ;; | 		git bisect--helper --bisect-run "$@" || exit;; | ||||||
| 	terms) | 	terms) | ||||||
| 		git bisect--helper --bisect-terms "$@" || exit;; | 		git bisect--helper --bisect-terms "$@" || exit;; | ||||||
| 	*) | 	*) | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user