bisect: simplify the addition of new bisect terms
We create a file BISECT_TERMS in the repository .git to be read during a bisection. There's no user-interface yet, but "git bisect" works if terms other than old/new or bad/good are set in .git/BISECT_TERMS. The fonctions to be changed if we add new terms are quite few. In git-bisect.sh: check_and_set_terms bisect_voc Co-authored-by: Louis Stuber <stuberl@ensimag.grenoble-inp.fr> Tweaked-by: Matthieu Moy <Matthieu.Moy@imag.fr> Signed-off-by: Antoine Delaite <antoine.delaite@ensimag.grenoble-inp.fr> Signed-off-by: Louis Stuber <stuberl@ensimag.grenoble-inp.fr> Signed-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr> Signed-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr> Signed-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr> Signed-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr> Signed-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									43f9d9f3a6
								
							
						
					
					
						commit
						cb46d630ba
					
				
							
								
								
									
										33
									
								
								bisect.c
								
								
								
								
							
							
						
						
									
										33
									
								
								bisect.c
								
								
								
								
							|  | @ -904,6 +904,36 @@ static void show_diff_tree(const char *prefix, struct commit *commit) | ||||||
| 	log_tree_commit(&opt, commit); | 	log_tree_commit(&opt, commit); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * The terms used for this bisect session are stored in BISECT_TERMS. | ||||||
|  |  * We read them and store them to adapt the messages accordingly. | ||||||
|  |  * Default is bad/good. | ||||||
|  |  */ | ||||||
|  | void read_bisect_terms(const char **read_bad, const char **read_good) | ||||||
|  | { | ||||||
|  | 	struct strbuf str = STRBUF_INIT; | ||||||
|  | 	const char *filename = git_path("BISECT_TERMS"); | ||||||
|  | 	FILE *fp = fopen(filename, "r"); | ||||||
|  |  | ||||||
|  | 	if (!fp) { | ||||||
|  | 		if (errno == ENOENT) { | ||||||
|  | 			*read_bad = "bad"; | ||||||
|  | 			*read_good = "good"; | ||||||
|  | 			return; | ||||||
|  | 		} else { | ||||||
|  | 			die("could not read file '%s': %s", filename, | ||||||
|  | 				strerror(errno)); | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		strbuf_getline(&str, fp, '\n'); | ||||||
|  | 		*read_bad = strbuf_detach(&str, NULL); | ||||||
|  | 		strbuf_getline(&str, fp, '\n'); | ||||||
|  | 		*read_good = strbuf_detach(&str, NULL); | ||||||
|  | 	} | ||||||
|  | 	strbuf_release(&str); | ||||||
|  | 	fclose(fp); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * We use the convention that exiting with an exit code 10 means that |  * We use the convention that exiting with an exit code 10 means that | ||||||
|  * the bisection process finished successfully. |  * the bisection process finished successfully. | ||||||
|  | @ -920,8 +950,7 @@ int bisect_next_all(const char *prefix, int no_checkout) | ||||||
| 	const unsigned char *bisect_rev; | 	const unsigned char *bisect_rev; | ||||||
| 	char bisect_rev_hex[GIT_SHA1_HEXSZ + 1]; | 	char bisect_rev_hex[GIT_SHA1_HEXSZ + 1]; | ||||||
|  |  | ||||||
| 	term_bad = "bad"; | 	read_bisect_terms(&term_bad, &term_good); | ||||||
| 	term_good = "good"; |  | ||||||
| 	if (read_bisect_refs()) | 	if (read_bisect_refs()) | ||||||
| 		die("reading bisect refs failed"); | 		die("reading bisect refs failed"); | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								bisect.h
								
								
								
								
							
							
						
						
									
										2
									
								
								bisect.h
								
								
								
								
							|  | @ -26,4 +26,6 @@ extern int bisect_next_all(const char *prefix, int no_checkout); | ||||||
|  |  | ||||||
| extern int estimate_bisect_steps(int all); | extern int estimate_bisect_steps(int all); | ||||||
|  |  | ||||||
|  | extern void read_bisect_terms(const char **bad, const char **good); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -77,6 +77,7 @@ bisect_start() { | ||||||
| 	orig_args=$(git rev-parse --sq-quote "$@") | 	orig_args=$(git rev-parse --sq-quote "$@") | ||||||
| 	bad_seen=0 | 	bad_seen=0 | ||||||
| 	eval='' | 	eval='' | ||||||
|  | 	must_write_terms=0 | ||||||
| 	if test "z$(git rev-parse --is-bare-repository)" != zfalse | 	if test "z$(git rev-parse --is-bare-repository)" != zfalse | ||||||
| 	then | 	then | ||||||
| 		mode=--no-checkout | 		mode=--no-checkout | ||||||
|  | @ -101,6 +102,14 @@ bisect_start() { | ||||||
| 				die "$(eval_gettext "'\$arg' does not appear to be a valid revision")" | 				die "$(eval_gettext "'\$arg' does not appear to be a valid revision")" | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			# The user ran "git bisect start <sha1> | ||||||
|  | 			# <sha1>", hence did not explicitly specify | ||||||
|  | 			# the terms, but we are already starting to | ||||||
|  | 			# set references named with the default terms, | ||||||
|  | 			# and won't be able to change afterwards. | ||||||
|  | 			must_write_terms=1 | ||||||
|  |  | ||||||
| 			case $bad_seen in | 			case $bad_seen in | ||||||
| 			0) state=$TERM_BAD ; bad_seen=1 ;; | 			0) state=$TERM_BAD ; bad_seen=1 ;; | ||||||
| 			*) state=$TERM_GOOD ;; | 			*) state=$TERM_GOOD ;; | ||||||
|  | @ -172,6 +181,10 @@ bisect_start() { | ||||||
| 	} && | 	} && | ||||||
| 	git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" && | 	git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" && | ||||||
| 	eval "$eval true" && | 	eval "$eval true" && | ||||||
|  | 	if test $must_write_terms -eq 1 | ||||||
|  | 	then | ||||||
|  | 		write_terms "$TERM_BAD" "$TERM_GOOD" | ||||||
|  | 	fi && | ||||||
| 	echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit | 	echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit | ||||||
| 	# | 	# | ||||||
| 	# Check if we can proceed to the next bisect state. | 	# Check if we can proceed to the next bisect state. | ||||||
|  | @ -232,6 +245,7 @@ bisect_skip() { | ||||||
| bisect_state() { | bisect_state() { | ||||||
| 	bisect_autostart | 	bisect_autostart | ||||||
| 	state=$1 | 	state=$1 | ||||||
|  | 	check_and_set_terms $state | ||||||
| 	case "$#,$state" in | 	case "$#,$state" in | ||||||
| 	0,*) | 	0,*) | ||||||
| 		die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;; | 		die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;; | ||||||
|  | @ -291,15 +305,17 @@ bisect_next_check() { | ||||||
| 		: bisect without $TERM_GOOD... | 		: bisect without $TERM_GOOD... | ||||||
| 		;; | 		;; | ||||||
| 	*) | 	*) | ||||||
|  | 		bad_syn=$(bisect_voc bad) | ||||||
|  | 		good_syn=$(bisect_voc good) | ||||||
| 		if test -s "$GIT_DIR/BISECT_START" | 		if test -s "$GIT_DIR/BISECT_START" | ||||||
| 		then | 		then | ||||||
| 			gettextln "You need to give me at least one good and one bad revision. |  | ||||||
| (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2 | 			eval_gettextln "You need to give me at least one \$bad_syn and one \$good_syn revision. | ||||||
|  | (You can use \"git bisect \$bad_syn\" and \"git bisect \$good_syn\" for that.)" >&2 | ||||||
| 		else | 		else | ||||||
| 			gettextln "You need to start by \"git bisect start\". | 			eval_gettextln "You need to start by \"git bisect start\". | ||||||
| You then need to give me at least one good and one bad revision. | You then need to give me at least one \$good_syn and one \$bad_syn revision. | ||||||
| (You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2 | (You can use \"git bisect \$bad_syn\" and \"git bisect \$good_syn\" for that.)" >&2 | ||||||
| 		fi | 		fi | ||||||
| 		exit 1 ;; | 		exit 1 ;; | ||||||
| 	esac | 	esac | ||||||
|  | @ -402,6 +418,7 @@ bisect_clean_state() { | ||||||
| 	rm -f "$GIT_DIR/BISECT_LOG" && | 	rm -f "$GIT_DIR/BISECT_LOG" && | ||||||
| 	rm -f "$GIT_DIR/BISECT_NAMES" && | 	rm -f "$GIT_DIR/BISECT_NAMES" && | ||||||
| 	rm -f "$GIT_DIR/BISECT_RUN" && | 	rm -f "$GIT_DIR/BISECT_RUN" && | ||||||
|  | 	rm -f "$GIT_DIR/BISECT_TERMS" && | ||||||
| 	# Cleanup head-name if it got left by an old version of git-bisect | 	# Cleanup head-name if it got left by an old version of git-bisect | ||||||
| 	rm -f "$GIT_DIR/head-name" && | 	rm -f "$GIT_DIR/head-name" && | ||||||
| 	git update-ref -d --no-deref BISECT_HEAD && | 	git update-ref -d --no-deref BISECT_HEAD && | ||||||
|  | @ -422,11 +439,13 @@ bisect_replay () { | ||||||
| 			rev="$command" | 			rev="$command" | ||||||
| 			command="$bisect" | 			command="$bisect" | ||||||
| 		fi | 		fi | ||||||
|  | 		get_terms | ||||||
|  | 		check_and_set_terms "$command" | ||||||
| 		case "$command" in | 		case "$command" in | ||||||
| 		start) | 		start) | ||||||
| 			cmd="bisect_start $rev" | 			cmd="bisect_start $rev" | ||||||
| 			eval "$cmd" ;; | 			eval "$cmd" ;; | ||||||
| 		$TERM_GOOD|$TERM_BAD|skip) | 		"$TERM_GOOD"|"$TERM_BAD"|skip) | ||||||
| 			bisect_write "$command" "$rev" ;; | 			bisect_write "$command" "$rev" ;; | ||||||
| 		*) | 		*) | ||||||
| 			die "$(gettext "?? what are you talking about?")" ;; | 			die "$(gettext "?? what are you talking about?")" ;; | ||||||
|  | @ -499,18 +518,62 @@ bisect_log () { | ||||||
| 	cat "$GIT_DIR/BISECT_LOG" | 	cat "$GIT_DIR/BISECT_LOG" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | get_terms () { | ||||||
|  | 	if test -s "$GIT_DIR/BISECT_TERMS" | ||||||
|  | 	then | ||||||
|  | 		{ | ||||||
|  | 		read TERM_BAD | ||||||
|  | 		read TERM_GOOD | ||||||
|  | 		} <"$GIT_DIR/BISECT_TERMS" | ||||||
|  | 	fi | ||||||
|  | } | ||||||
|  |  | ||||||
|  | write_terms () { | ||||||
|  | 	TERM_BAD=$1 | ||||||
|  | 	TERM_GOOD=$2 | ||||||
|  | 	printf '%s\n%s\n' "$TERM_BAD" "$TERM_GOOD" >"$GIT_DIR/BISECT_TERMS" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | check_and_set_terms () { | ||||||
|  | 	cmd="$1" | ||||||
|  | 	case "$cmd" in | ||||||
|  | 	skip|start|terms) ;; | ||||||
|  | 	*) | ||||||
|  | 		if test -s "$GIT_DIR/BISECT_TERMS" && test "$cmd" != "$TERM_BAD" && test "$cmd" != "$TERM_GOOD" | ||||||
|  | 		then | ||||||
|  | 			die "$(eval_gettext "Invalid command: you're currently in a \$TERM_BAD/\$TERM_GOOD bisect.")" | ||||||
|  | 		fi | ||||||
|  | 		case "$cmd" in | ||||||
|  | 		bad|good) | ||||||
|  | 			if ! test -s "$GIT_DIR/BISECT_TERMS" | ||||||
|  | 			then | ||||||
|  | 				write_terms bad good | ||||||
|  | 			fi | ||||||
|  | 			;; | ||||||
|  | 		esac ;; | ||||||
|  | 	esac | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bisect_voc () { | ||||||
|  | 	case "$1" in | ||||||
|  | 	bad) echo "bad" ;; | ||||||
|  | 	good) echo "good" ;; | ||||||
|  | 	esac | ||||||
|  | } | ||||||
|  |  | ||||||
| case "$#" in | case "$#" in | ||||||
| 0) | 0) | ||||||
| 	usage ;; | 	usage ;; | ||||||
| *) | *) | ||||||
| 	cmd="$1" | 	cmd="$1" | ||||||
|  | 	get_terms | ||||||
| 	shift | 	shift | ||||||
| 	case "$cmd" in | 	case "$cmd" in | ||||||
| 	help) | 	help) | ||||||
| 		git bisect -h ;; | 		git bisect -h ;; | ||||||
| 	start) | 	start) | ||||||
| 		bisect_start "$@" ;; | 		bisect_start "$@" ;; | ||||||
| 	bad|good) | 	bad|good|"$TERM_BAD"|"$TERM_GOOD") | ||||||
| 		bisect_state "$cmd" "$@" ;; | 		bisect_state "$cmd" "$@" ;; | ||||||
| 	skip) | 	skip) | ||||||
| 		bisect_skip "$@" ;; | 		bisect_skip "$@" ;; | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								revision.c
								
								
								
								
							
							
						
						
									
										18
									
								
								revision.c
								
								
								
								
							|  | @ -18,9 +18,13 @@ | ||||||
| #include "commit-slab.h" | #include "commit-slab.h" | ||||||
| #include "dir.h" | #include "dir.h" | ||||||
| #include "cache-tree.h" | #include "cache-tree.h" | ||||||
|  | #include "bisect.h" | ||||||
|  |  | ||||||
| volatile show_early_output_fn_t show_early_output; | volatile show_early_output_fn_t show_early_output; | ||||||
|  |  | ||||||
|  | static const char *term_bad; | ||||||
|  | static const char *term_good; | ||||||
|  |  | ||||||
| char *path_name(const struct name_path *path, const char *name) | char *path_name(const struct name_path *path, const char *name) | ||||||
| { | { | ||||||
| 	const struct name_path *p; | 	const struct name_path *p; | ||||||
|  | @ -2076,14 +2080,23 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx, | ||||||
| 	ctx->argc -= n; | 	ctx->argc -= n; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int for_each_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data, const char *term) { | ||||||
|  | 	struct strbuf bisect_refs = STRBUF_INIT; | ||||||
|  | 	int status; | ||||||
|  | 	strbuf_addf(&bisect_refs, "refs/bisect/%s", term); | ||||||
|  | 	status = for_each_ref_in_submodule(submodule, bisect_refs.buf, fn, cb_data); | ||||||
|  | 	strbuf_release(&bisect_refs); | ||||||
|  | 	return status; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data) | static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data) | ||||||
| { | { | ||||||
| 	return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data); | 	return for_each_bisect_ref(submodule, fn, cb_data, term_bad); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data) | static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data) | ||||||
| { | { | ||||||
| 	return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data); | 	return for_each_bisect_ref(submodule, fn, cb_data, term_good); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int handle_revision_pseudo_opt(const char *submodule, | static int handle_revision_pseudo_opt(const char *submodule, | ||||||
|  | @ -2112,6 +2125,7 @@ static int handle_revision_pseudo_opt(const char *submodule, | ||||||
| 		handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule); | 		handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule); | ||||||
| 		clear_ref_exclusion(&revs->ref_excludes); | 		clear_ref_exclusion(&revs->ref_excludes); | ||||||
| 	} else if (!strcmp(arg, "--bisect")) { | 	} else if (!strcmp(arg, "--bisect")) { | ||||||
|  | 		read_bisect_terms(&term_bad, &term_good); | ||||||
| 		handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref); | 		handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref); | ||||||
| 		handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref); | 		handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref); | ||||||
| 		revs->bisect = 1; | 		revs->bisect = 1; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Antoine Delaite
						Antoine Delaite