Skip to content

Commit dbeaf8e

Browse files
phillipwoodgitster
authored andcommitted
checkout: cleanup --conflict=<style> parsing
Passing an invalid conflict style name such as "--conflict=bad" gives the error message error: unknown style 'bad' given for 'merge.conflictstyle' which is unfortunate as it talks about a config setting rather than the option given on the command line. This happens because the implementation calls git_xmerge_config() to set the conflict style using the value given on the command line. Use the newly added parse_conflict_style_name() instead and pass the value down the call chain to override the config setting. This also means we can avoid setting up a struct config_context required for calling git_xmerge_config(). The option is now parsed in a callback to avoid having to store the option name. This is a change in behavior as now git checkout --conflict=bad --conflict=diff3 will error out when parsing "--conflict=bad" whereas before this change it would succeed because it would only try to parse the value of the last "--conflict" option given on the command line. Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 135cc71 commit dbeaf8e

File tree

2 files changed

+38
-19
lines changed

2 files changed

+38
-19
lines changed

builtin/checkout.c

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ struct checkout_opts {
9191
int new_branch_log;
9292
enum branch_track track;
9393
struct diff_options diff_options;
94-
char *conflict_style;
94+
int conflict_style;
9595

9696
int branch_exists;
9797
const char *prefix;
@@ -100,6 +100,8 @@ struct checkout_opts {
100100
struct tree *source_tree;
101101
};
102102

103+
#define CHECKOUT_OPTS_INIT { .conflict_style = -1 }
104+
103105
struct branch_info {
104106
char *name; /* The short name used */
105107
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,
251253
}
252254

253255
static int checkout_merged(int pos, const struct checkout *state,
254-
int *nr_checkouts, struct mem_pool *ce_mem_pool)
256+
int *nr_checkouts, struct mem_pool *ce_mem_pool,
257+
int conflict_style)
255258
{
256259
struct cache_entry *ce = the_index.cache[pos];
257260
const char *path = ce->name;
@@ -286,6 +289,7 @@ static int checkout_merged(int pos, const struct checkout *state,
286289

287290
git_config_get_bool("merge.renormalize", &renormalize);
288291
ll_opts.renormalize = renormalize;
292+
ll_opts.conflict_style = conflict_style;
289293
merge_status = ll_merge(&result_buf, path, &ancestor, "base",
290294
&ours, "ours", &theirs, "theirs",
291295
state->istate, &ll_opts);
@@ -416,7 +420,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
416420
else if (opts->merge)
417421
errs |= checkout_merged(pos, &state,
418422
&nr_unmerged,
419-
&ce_mem_pool);
423+
&ce_mem_pool,
424+
opts->conflict_style);
420425
pos = skip_same_name(ce, pos) - 1;
421426
}
422427
}
@@ -886,6 +891,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
886891
}
887892
o.branch1 = new_branch_info->name;
888893
o.branch2 = "local";
894+
o.conflict_style = opts->conflict_style;
889895
ret = merge_trees(&o,
890896
new_tree,
891897
work,
@@ -1616,6 +1622,21 @@ static int checkout_branch(struct checkout_opts *opts,
16161622
return switch_branches(opts, new_branch_info);
16171623
}
16181624

1625+
static int parse_opt_conflict(const struct option *o, const char *arg, int unset)
1626+
{
1627+
struct checkout_opts *opts = o->value;
1628+
1629+
if (unset) {
1630+
opts->conflict_style = -1;
1631+
return 0;
1632+
}
1633+
opts->conflict_style = parse_conflict_style_name(arg);
1634+
if (opts->conflict_style < 0)
1635+
return error(_("unknown conflict style '%s'"), arg);
1636+
1637+
return 0;
1638+
}
1639+
16191640
static struct option *add_common_options(struct checkout_opts *opts,
16201641
struct option *prevopts)
16211642
{
@@ -1626,8 +1647,9 @@ static struct option *add_common_options(struct checkout_opts *opts,
16261647
PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
16271648
OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
16281649
OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
1629-
OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
1630-
N_("conflict style (merge, diff3, or zdiff3)")),
1650+
OPT_CALLBACK(0, "conflict", opts, N_("style"),
1651+
N_("conflict style (merge, diff3, or zdiff3)"),
1652+
parse_opt_conflict),
16311653
OPT_END()
16321654
};
16331655
struct option *newopts = parse_options_concat(prevopts, options);
@@ -1718,15 +1740,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
17181740
opts->show_progress = isatty(2);
17191741
}
17201742

1721-
if (opts->conflict_style) {
1722-
struct key_value_info kvi = KVI_INIT;
1723-
struct config_context ctx = {
1724-
.kvi = &kvi,
1725-
};
1743+
if (opts->conflict_style >= 0)
17261744
opts->merge = 1; /* implied */
1727-
git_xmerge_config("merge.conflictstyle", opts->conflict_style,
1728-
&ctx, NULL);
1729-
}
1745+
17301746
if (opts->force) {
17311747
opts->discard_changes = 1;
17321748
opts->ignore_unmerged_opt = "--force";
@@ -1891,7 +1907,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
18911907

18921908
int cmd_checkout(int argc, const char **argv, const char *prefix)
18931909
{
1894-
struct checkout_opts opts;
1910+
struct checkout_opts opts = CHECKOUT_OPTS_INIT;
18951911
struct option *options;
18961912
struct option checkout_options[] = {
18971913
OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -1907,7 +1923,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
19071923
int ret;
19081924
struct branch_info new_branch_info = { 0 };
19091925

1910-
memset(&opts, 0, sizeof(opts));
19111926
opts.dwim_new_local_branch = 1;
19121927
opts.switch_branch_doing_nothing_is_ok = 1;
19131928
opts.only_merge_on_switching_branches = 0;
@@ -1946,7 +1961,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
19461961

19471962
int cmd_switch(int argc, const char **argv, const char *prefix)
19481963
{
1949-
struct checkout_opts opts;
1964+
struct checkout_opts opts = CHECKOUT_OPTS_INIT;
19501965
struct option *options = NULL;
19511966
struct option switch_options[] = {
19521967
OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
@@ -1962,7 +1977,6 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
19621977
int ret;
19631978
struct branch_info new_branch_info = { 0 };
19641979

1965-
memset(&opts, 0, sizeof(opts));
19661980
opts.dwim_new_local_branch = 1;
19671981
opts.accept_ref = 1;
19681982
opts.accept_pathspec = 0;
@@ -1988,7 +2002,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
19882002

19892003
int cmd_restore(int argc, const char **argv, const char *prefix)
19902004
{
1991-
struct checkout_opts opts;
2005+
struct checkout_opts opts = CHECKOUT_OPTS_INIT;
19922006
struct option *options;
19932007
struct option restore_options[] = {
19942008
OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
@@ -2005,7 +2019,6 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
20052019
int ret;
20062020
struct branch_info new_branch_info = { 0 };
20072021

2008-
memset(&opts, 0, sizeof(opts));
20092022
opts.accept_ref = 0;
20102023
opts.accept_pathspec = 1;
20112024
opts.empty_pathspec_ok = 0;

t/t7201-co.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,12 @@ test_expect_success 'checkout --conflict=diff3' '
631631
test_cmp merged file
632632
'
633633

634+
test_expect_success 'checkout with invalid conflict style' '
635+
test_must_fail git checkout --conflict=bad 2>actual -- file &&
636+
echo "error: unknown conflict style ${SQ}bad${SQ}" >expect &&
637+
test_cmp expect actual
638+
'
639+
634640
test_expect_success 'failing checkout -b should not break working tree' '
635641
git clean -fd && # Remove untracked files in the way
636642
git reset --hard main &&

0 commit comments

Comments
 (0)