Merge branch 'pw/checkout-conflict-errorfix'
"git checkout --conflict=bad" reported a bad conflictStyle as if it were given to a configuration variable; it has been corrected to report that the command line option is bad. * pw/checkout-conflict-errorfix: checkout: fix interaction between --conflict and --merge checkout: cleanup --conflict=<style> parsing merge options: add a conflict style member merge-ll: introduce LL_MERGE_OPTIONS_INIT xdiff-interface: refactor parsing of merge.conflictstylemaint
						commit
						ccdc7d98bb
					
				|  | @ -91,7 +91,7 @@ struct checkout_opts { | |||
| 	int new_branch_log; | ||||
| 	enum branch_track track; | ||||
| 	struct diff_options diff_options; | ||||
| 	char *conflict_style; | ||||
| 	int conflict_style; | ||||
|  | ||||
| 	int branch_exists; | ||||
| 	const char *prefix; | ||||
|  | @ -100,6 +100,8 @@ struct checkout_opts { | |||
| 	struct tree *source_tree; | ||||
| }; | ||||
|  | ||||
| #define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 } | ||||
|  | ||||
| struct branch_info { | ||||
| 	char *name; /* The short name used */ | ||||
| 	char *path; /* The full name of a real branch */ | ||||
|  | @ -251,7 +253,8 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos, | |||
| } | ||||
|  | ||||
| static int checkout_merged(int pos, const struct checkout *state, | ||||
| 			   int *nr_checkouts, struct mem_pool *ce_mem_pool) | ||||
| 			   int *nr_checkouts, struct mem_pool *ce_mem_pool, | ||||
| 			   int conflict_style) | ||||
| { | ||||
| 	struct cache_entry *ce = the_index.cache[pos]; | ||||
| 	const char *path = ce->name; | ||||
|  | @ -262,7 +265,7 @@ static int checkout_merged(int pos, const struct checkout *state, | |||
| 	mmbuffer_t result_buf; | ||||
| 	struct object_id threeway[3]; | ||||
| 	unsigned mode = 0; | ||||
| 	struct ll_merge_options ll_opts; | ||||
| 	struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT; | ||||
| 	int renormalize = 0; | ||||
|  | ||||
| 	memset(threeway, 0, sizeof(threeway)); | ||||
|  | @ -284,9 +287,9 @@ static int checkout_merged(int pos, const struct checkout *state, | |||
| 	read_mmblob(&ours, &threeway[1]); | ||||
| 	read_mmblob(&theirs, &threeway[2]); | ||||
|  | ||||
| 	memset(&ll_opts, 0, sizeof(ll_opts)); | ||||
| 	git_config_get_bool("merge.renormalize", &renormalize); | ||||
| 	ll_opts.renormalize = renormalize; | ||||
| 	ll_opts.conflict_style = conflict_style; | ||||
| 	merge_status = ll_merge(&result_buf, path, &ancestor, "base", | ||||
| 				&ours, "ours", &theirs, "theirs", | ||||
| 				state->istate, &ll_opts); | ||||
|  | @ -417,7 +420,8 @@ static int checkout_worktree(const struct checkout_opts *opts, | |||
| 			else if (opts->merge) | ||||
| 				errs |= checkout_merged(pos, &state, | ||||
| 							&nr_unmerged, | ||||
| 							&ce_mem_pool); | ||||
| 							&ce_mem_pool, | ||||
| 							opts->conflict_style); | ||||
| 			pos = skip_same_name(ce, pos) - 1; | ||||
| 		} | ||||
| 	} | ||||
|  | @ -897,6 +901,7 @@ static int merge_working_tree(const struct checkout_opts *opts, | |||
| 			} | ||||
| 			o.branch1 = new_branch_info->name; | ||||
| 			o.branch2 = "local"; | ||||
| 			o.conflict_style = opts->conflict_style; | ||||
| 			ret = merge_trees(&o, | ||||
| 					  new_tree, | ||||
| 					  work, | ||||
|  | @ -1634,6 +1639,24 @@ static int checkout_branch(struct checkout_opts *opts, | |||
| 	return switch_branches(opts, new_branch_info); | ||||
| } | ||||
|  | ||||
| static int parse_opt_conflict(const struct option *o, const char *arg, int unset) | ||||
| { | ||||
| 	struct checkout_opts *opts = o->value; | ||||
|  | ||||
| 	if (unset) { | ||||
| 		opts->conflict_style = -1; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	opts->conflict_style = parse_conflict_style_name(arg); | ||||
| 	if (opts->conflict_style < 0) | ||||
| 		return error(_("unknown conflict style '%s'"), arg); | ||||
| 	/* --conflict overrides a previous --no-merge */ | ||||
| 	if (!opts->merge) | ||||
| 		opts->merge = -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct option *add_common_options(struct checkout_opts *opts, | ||||
| 					 struct option *prevopts) | ||||
| { | ||||
|  | @ -1644,8 +1667,9 @@ static struct option *add_common_options(struct checkout_opts *opts, | |||
| 			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), | ||||
| 		OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")), | ||||
| 		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")), | ||||
| 		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"), | ||||
| 			   N_("conflict style (merge, diff3, or zdiff3)")), | ||||
| 		OPT_CALLBACK(0, "conflict", opts, N_("style"), | ||||
| 			     N_("conflict style (merge, diff3, or zdiff3)"), | ||||
| 			     parse_opt_conflict), | ||||
| 		OPT_END() | ||||
| 	}; | ||||
| 	struct option *newopts = parse_options_concat(prevopts, options); | ||||
|  | @ -1737,15 +1761,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix, | |||
| 			opts->show_progress = isatty(2); | ||||
| 	} | ||||
|  | ||||
| 	if (opts->conflict_style) { | ||||
| 		struct key_value_info kvi = KVI_INIT; | ||||
| 		struct config_context ctx = { | ||||
| 			.kvi = &kvi, | ||||
| 		}; | ||||
| 		opts->merge = 1; /* implied */ | ||||
| 		git_xmerge_config("merge.conflictstyle", opts->conflict_style, | ||||
| 				  &ctx, NULL); | ||||
| 	} | ||||
| 	/* --conflicts implies --merge */ | ||||
| 	if (opts->merge == -1) | ||||
| 		opts->merge = opts->conflict_style >= 0; | ||||
|  | ||||
| 	if (opts->force) { | ||||
| 		opts->discard_changes = 1; | ||||
| 		opts->ignore_unmerged_opt = "--force"; | ||||
|  | @ -1917,7 +1936,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, | |||
|  | ||||
| int cmd_checkout(int argc, const char **argv, const char *prefix) | ||||
| { | ||||
| 	struct checkout_opts opts; | ||||
| 	struct checkout_opts opts = CHECKOUT_OPTS_INIT; | ||||
| 	struct option *options; | ||||
| 	struct option checkout_options[] = { | ||||
| 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), | ||||
|  | @ -1931,7 +1950,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) | |||
| 		OPT_END() | ||||
| 	}; | ||||
|  | ||||
| 	memset(&opts, 0, sizeof(opts)); | ||||
| 	opts.dwim_new_local_branch = 1; | ||||
| 	opts.switch_branch_doing_nothing_is_ok = 1; | ||||
| 	opts.only_merge_on_switching_branches = 0; | ||||
|  | @ -1965,7 +1983,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) | |||
|  | ||||
| int cmd_switch(int argc, const char **argv, const char *prefix) | ||||
| { | ||||
| 	struct checkout_opts opts; | ||||
| 	struct checkout_opts opts = CHECKOUT_OPTS_INIT; | ||||
| 	struct option *options = NULL; | ||||
| 	struct option switch_options[] = { | ||||
| 		OPT_STRING('c', "create", &opts.new_branch, N_("branch"), | ||||
|  | @ -1979,7 +1997,6 @@ int cmd_switch(int argc, const char **argv, const char *prefix) | |||
| 		OPT_END() | ||||
| 	}; | ||||
|  | ||||
| 	memset(&opts, 0, sizeof(opts)); | ||||
| 	opts.dwim_new_local_branch = 1; | ||||
| 	opts.accept_ref = 1; | ||||
| 	opts.accept_pathspec = 0; | ||||
|  | @ -2002,7 +2019,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix) | |||
|  | ||||
| int cmd_restore(int argc, const char **argv, const char *prefix) | ||||
| { | ||||
| 	struct checkout_opts opts; | ||||
| 	struct checkout_opts opts = CHECKOUT_OPTS_INIT; | ||||
| 	struct option *options; | ||||
| 	struct option restore_options[] = { | ||||
| 		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>", | ||||
|  | @ -2017,7 +2034,6 @@ int cmd_restore(int argc, const char **argv, const char *prefix) | |||
| 		OPT_END() | ||||
| 	}; | ||||
|  | ||||
| 	memset(&opts, 0, sizeof(opts)); | ||||
| 	opts.accept_ref = 0; | ||||
| 	opts.accept_pathspec = 1; | ||||
| 	opts.empty_pathspec_ok = 0; | ||||
|  |  | |||
|  | @ -128,7 +128,9 @@ static enum ll_merge_result ll_xdl_merge(const struct ll_merge_driver *drv_unuse | |||
| 	xmp.level = XDL_MERGE_ZEALOUS; | ||||
| 	xmp.favor = opts->variant; | ||||
| 	xmp.xpp.flags = opts->xdl_opts; | ||||
| 	if (git_xmerge_style >= 0) | ||||
| 	if (opts->conflict_style >= 0) | ||||
| 		xmp.style = opts->conflict_style; | ||||
| 	else if (git_xmerge_style >= 0) | ||||
| 		xmp.style = git_xmerge_style; | ||||
| 	if (marker_size > 0) | ||||
| 		xmp.marker_size = marker_size; | ||||
|  | @ -401,7 +403,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf, | |||
| 	     const struct ll_merge_options *opts) | ||||
| { | ||||
| 	struct attr_check *check = load_merge_attributes(); | ||||
| 	static const struct ll_merge_options default_opts; | ||||
| 	static const struct ll_merge_options default_opts = LL_MERGE_OPTIONS_INIT; | ||||
| 	const char *ll_driver_name = NULL; | ||||
| 	int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; | ||||
| 	const struct ll_merge_driver *driver; | ||||
|  |  | |||
|  | @ -78,10 +78,15 @@ struct ll_merge_options { | |||
| 	 */ | ||||
| 	unsigned extra_marker_size; | ||||
|  | ||||
| 	/* Override the global conflict style. */ | ||||
| 	int conflict_style; | ||||
|  | ||||
| 	/* Extra xpparam_t flags as defined in xdiff/xdiff.h. */ | ||||
| 	long xdl_opts; | ||||
| }; | ||||
|  | ||||
| #define LL_MERGE_OPTIONS_INIT { .conflict_style = -1 } | ||||
|  | ||||
| enum ll_merge_result { | ||||
| 	LL_MERGE_ERROR = -1, | ||||
| 	LL_MERGE_OK = 0, | ||||
|  |  | |||
|  | @ -2025,7 +2025,7 @@ static int merge_3way(struct merge_options *opt, | |||
| 		      mmbuffer_t *result_buf) | ||||
| { | ||||
| 	mmfile_t orig, src1, src2; | ||||
| 	struct ll_merge_options ll_opts = {0}; | ||||
| 	struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT; | ||||
| 	char *base, *name1, *name2; | ||||
| 	enum ll_merge_result merge_status; | ||||
|  | ||||
|  | @ -2035,6 +2035,7 @@ static int merge_3way(struct merge_options *opt, | |||
| 	ll_opts.renormalize = opt->renormalize; | ||||
| 	ll_opts.extra_marker_size = extra_marker_size; | ||||
| 	ll_opts.xdl_opts = opt->xdl_opts; | ||||
| 	ll_opts.conflict_style = opt->conflict_style; | ||||
|  | ||||
| 	if (opt->priv->call_depth) { | ||||
| 		ll_opts.virtual_ancestor = 1; | ||||
|  |  | |||
|  | @ -1048,13 +1048,14 @@ static int merge_3way(struct merge_options *opt, | |||
| 		      const int extra_marker_size) | ||||
| { | ||||
| 	mmfile_t orig, src1, src2; | ||||
| 	struct ll_merge_options ll_opts = {0}; | ||||
| 	struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT; | ||||
| 	char *base, *name1, *name2; | ||||
| 	enum ll_merge_result merge_status; | ||||
|  | ||||
| 	ll_opts.renormalize = opt->renormalize; | ||||
| 	ll_opts.extra_marker_size = extra_marker_size; | ||||
| 	ll_opts.xdl_opts = opt->xdl_opts; | ||||
| 	ll_opts.conflict_style = opt->conflict_style; | ||||
|  | ||||
| 	if (opt->priv->call_depth) { | ||||
| 		ll_opts.virtual_ancestor = 1; | ||||
|  | @ -3947,6 +3948,8 @@ void init_merge_options(struct merge_options *opt, | |||
|  | ||||
| 	opt->renormalize = 0; | ||||
|  | ||||
| 	opt->conflict_style = -1; | ||||
|  | ||||
| 	merge_recursive_config(opt); | ||||
| 	merge_verbosity = getenv("GIT_MERGE_VERBOSITY"); | ||||
| 	if (merge_verbosity) | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ struct merge_options { | |||
|  | ||||
| 	/* xdiff-related options (patience, ignore whitespace, ours/theirs) */ | ||||
| 	long xdl_opts; | ||||
| 	int conflict_style; | ||||
| 	enum { | ||||
| 		MERGE_VARIANT_NORMAL = 0, | ||||
| 		MERGE_VARIANT_OURS, | ||||
|  |  | |||
|  | @ -631,6 +631,72 @@ test_expect_success 'checkout --conflict=diff3' ' | |||
| 	test_cmp merged file | ||||
| ' | ||||
|  | ||||
| test_expect_success 'checkout --conflict=diff3 --no-conflict does not merge' ' | ||||
| 	setup_conflicting_index && | ||||
| 	echo "none of the above" >expect && | ||||
| 	cat expect >fild && | ||||
| 	cat expect >file && | ||||
| 	test_must_fail git checkout --conflict=diff3 --no-conflict -- fild file 2>err && | ||||
| 	test_cmp expect file && | ||||
| 	test_cmp expect fild && | ||||
| 	echo "error: path ${SQ}file${SQ} is unmerged" >expect && | ||||
| 	test_cmp expect err | ||||
| ' | ||||
|  | ||||
| test_expect_success 'checkout --conflict=diff3 --no-merge does not merge' ' | ||||
| 	setup_conflicting_index && | ||||
| 	echo "none of the above" >expect && | ||||
| 	cat expect >fild && | ||||
| 	cat expect >file && | ||||
| 	test_must_fail git checkout --conflict=diff3 --no-merge -- fild file 2>err && | ||||
| 	test_cmp expect file && | ||||
| 	test_cmp expect fild && | ||||
| 	echo "error: path ${SQ}file${SQ} is unmerged" >expect && | ||||
| 	test_cmp expect err | ||||
| ' | ||||
|  | ||||
| test_expect_success 'checkout --no-merge --conflict=diff3 does merge' ' | ||||
| 	setup_conflicting_index && | ||||
| 	echo "none of the above" >fild && | ||||
| 	echo "none of the above" >file && | ||||
| 	git checkout --no-merge --conflict=diff3 -- fild file && | ||||
| 	echo "ourside" >expect && | ||||
| 	test_cmp expect fild && | ||||
| 	cat >expect <<-\EOF && | ||||
| 	<<<<<<< ours | ||||
| 	ourside | ||||
| 	||||||| base | ||||
| 	original | ||||
| 	======= | ||||
| 	theirside | ||||
| 	>>>>>>> theirs | ||||
| 	EOF | ||||
| 	test_cmp expect file | ||||
| ' | ||||
|  | ||||
| test_expect_success 'checkout --merge --conflict=diff3 --no-conflict does merge' ' | ||||
| 	setup_conflicting_index && | ||||
| 	echo "none of the above" >fild && | ||||
| 	echo "none of the above" >file && | ||||
| 	git checkout --merge --conflict=diff3 --no-conflict -- fild file && | ||||
| 	echo "ourside" >expect && | ||||
| 	test_cmp expect fild && | ||||
| 	cat >expect <<-\EOF && | ||||
| 	<<<<<<< ours | ||||
| 	ourside | ||||
| 	======= | ||||
| 	theirside | ||||
| 	>>>>>>> theirs | ||||
| 	EOF | ||||
| 	test_cmp expect file | ||||
| ' | ||||
|  | ||||
| test_expect_success 'checkout with invalid conflict style' ' | ||||
| 	test_must_fail git checkout --conflict=bad 2>actual -- file && | ||||
| 	echo "error: unknown conflict style ${SQ}bad${SQ}" >expect && | ||||
| 	test_cmp expect actual | ||||
| ' | ||||
|  | ||||
| test_expect_success 'failing checkout -b should not break working tree' ' | ||||
| 	git clean -fd &&  # Remove untracked files in the way | ||||
| 	git reset --hard main && | ||||
|  |  | |||
|  | @ -305,6 +305,22 @@ int xdiff_compare_lines(const char *l1, long s1, | |||
| 	return xdl_recmatch(l1, s1, l2, s2, flags); | ||||
| } | ||||
|  | ||||
| int parse_conflict_style_name(const char *value) | ||||
| { | ||||
| 	if (!strcmp(value, "diff3")) | ||||
| 		return XDL_MERGE_DIFF3; | ||||
| 	else if (!strcmp(value, "zdiff3")) | ||||
| 		return XDL_MERGE_ZEALOUS_DIFF3; | ||||
| 	else if (!strcmp(value, "merge")) | ||||
| 		return 0; | ||||
| 	/* | ||||
| 	 * Please update _git_checkout() in git-completion.bash when | ||||
| 	 * you add new merge config | ||||
| 	 */ | ||||
| 	else | ||||
| 		return -1; | ||||
| } | ||||
|  | ||||
| int git_xmerge_style = -1; | ||||
|  | ||||
| int git_xmerge_config(const char *var, const char *value, | ||||
|  | @ -313,17 +329,8 @@ int git_xmerge_config(const char *var, const char *value, | |||
| 	if (!strcmp(var, "merge.conflictstyle")) { | ||||
| 		if (!value) | ||||
| 			return config_error_nonbool(var); | ||||
| 		if (!strcmp(value, "diff3")) | ||||
| 			git_xmerge_style = XDL_MERGE_DIFF3; | ||||
| 		else if (!strcmp(value, "zdiff3")) | ||||
| 			git_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3; | ||||
| 		else if (!strcmp(value, "merge")) | ||||
| 			git_xmerge_style = 0; | ||||
| 		/* | ||||
| 		 * Please update _git_checkout() in | ||||
| 		 * git-completion.bash when you add new merge config | ||||
| 		 */ | ||||
| 		else | ||||
| 		git_xmerge_style = parse_conflict_style_name(value); | ||||
| 		if (git_xmerge_style == -1) | ||||
| 			return error(_("unknown style '%s' given for '%s'"), | ||||
| 				     value, var); | ||||
| 		return 0; | ||||
|  |  | |||
|  | @ -51,6 +51,7 @@ int buffer_is_binary(const char *ptr, unsigned long size); | |||
| void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags); | ||||
| void xdiff_clear_find_func(xdemitconf_t *xecfg); | ||||
| struct config_context; | ||||
| int parse_conflict_style_name(const char *value); | ||||
| int git_xmerge_config(const char *var, const char *value, | ||||
| 		      const struct config_context *ctx, void *cb); | ||||
| extern int git_xmerge_style; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano