Skip to content

Commit ccdc7d9

Browse files
committed
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.conflictstyle
2 parents d6fd043 + 5a99c1a commit ccdc7d9

File tree

9 files changed

+139
-37
lines changed

9 files changed

+139
-37
lines changed

builtin/checkout.c

Lines changed: 38 additions & 22 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, .merge = -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;
@@ -262,7 +265,7 @@ static int checkout_merged(int pos, const struct checkout *state,
262265
mmbuffer_t result_buf;
263266
struct object_id threeway[3];
264267
unsigned mode = 0;
265-
struct ll_merge_options ll_opts;
268+
struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
266269
int renormalize = 0;
267270

268271
memset(threeway, 0, sizeof(threeway));
@@ -284,9 +287,9 @@ static int checkout_merged(int pos, const struct checkout *state,
284287
read_mmblob(&ours, &threeway[1]);
285288
read_mmblob(&theirs, &threeway[2]);
286289

287-
memset(&ll_opts, 0, sizeof(ll_opts));
288290
git_config_get_bool("merge.renormalize", &renormalize);
289291
ll_opts.renormalize = renormalize;
292+
ll_opts.conflict_style = conflict_style;
290293
merge_status = ll_merge(&result_buf, path, &ancestor, "base",
291294
&ours, "ours", &theirs, "theirs",
292295
state->istate, &ll_opts);
@@ -417,7 +420,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
417420
else if (opts->merge)
418421
errs |= checkout_merged(pos, &state,
419422
&nr_unmerged,
420-
&ce_mem_pool);
423+
&ce_mem_pool,
424+
opts->conflict_style);
421425
pos = skip_same_name(ce, pos) - 1;
422426
}
423427
}
@@ -897,6 +901,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
897901
}
898902
o.branch1 = new_branch_info->name;
899903
o.branch2 = "local";
904+
o.conflict_style = opts->conflict_style;
900905
ret = merge_trees(&o,
901906
new_tree,
902907
work,
@@ -1634,6 +1639,24 @@ static int checkout_branch(struct checkout_opts *opts,
16341639
return switch_branches(opts, new_branch_info);
16351640
}
16361641

1642+
static int parse_opt_conflict(const struct option *o, const char *arg, int unset)
1643+
{
1644+
struct checkout_opts *opts = o->value;
1645+
1646+
if (unset) {
1647+
opts->conflict_style = -1;
1648+
return 0;
1649+
}
1650+
opts->conflict_style = parse_conflict_style_name(arg);
1651+
if (opts->conflict_style < 0)
1652+
return error(_("unknown conflict style '%s'"), arg);
1653+
/* --conflict overrides a previous --no-merge */
1654+
if (!opts->merge)
1655+
opts->merge = -1;
1656+
1657+
return 0;
1658+
}
1659+
16371660
static struct option *add_common_options(struct checkout_opts *opts,
16381661
struct option *prevopts)
16391662
{
@@ -1644,8 +1667,9 @@ static struct option *add_common_options(struct checkout_opts *opts,
16441667
PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
16451668
OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
16461669
OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
1647-
OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
1648-
N_("conflict style (merge, diff3, or zdiff3)")),
1670+
OPT_CALLBACK(0, "conflict", opts, N_("style"),
1671+
N_("conflict style (merge, diff3, or zdiff3)"),
1672+
parse_opt_conflict),
16491673
OPT_END()
16501674
};
16511675
struct option *newopts = parse_options_concat(prevopts, options);
@@ -1737,15 +1761,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
17371761
opts->show_progress = isatty(2);
17381762
}
17391763

1740-
if (opts->conflict_style) {
1741-
struct key_value_info kvi = KVI_INIT;
1742-
struct config_context ctx = {
1743-
.kvi = &kvi,
1744-
};
1745-
opts->merge = 1; /* implied */
1746-
git_xmerge_config("merge.conflictstyle", opts->conflict_style,
1747-
&ctx, NULL);
1748-
}
1764+
/* --conflicts implies --merge */
1765+
if (opts->merge == -1)
1766+
opts->merge = opts->conflict_style >= 0;
1767+
17491768
if (opts->force) {
17501769
opts->discard_changes = 1;
17511770
opts->ignore_unmerged_opt = "--force";
@@ -1917,7 +1936,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
19171936

19181937
int cmd_checkout(int argc, const char **argv, const char *prefix)
19191938
{
1920-
struct checkout_opts opts;
1939+
struct checkout_opts opts = CHECKOUT_OPTS_INIT;
19211940
struct option *options;
19221941
struct option checkout_options[] = {
19231942
OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -1931,7 +1950,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
19311950
OPT_END()
19321951
};
19331952

1934-
memset(&opts, 0, sizeof(opts));
19351953
opts.dwim_new_local_branch = 1;
19361954
opts.switch_branch_doing_nothing_is_ok = 1;
19371955
opts.only_merge_on_switching_branches = 0;
@@ -1965,7 +1983,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
19651983

19661984
int cmd_switch(int argc, const char **argv, const char *prefix)
19671985
{
1968-
struct checkout_opts opts;
1986+
struct checkout_opts opts = CHECKOUT_OPTS_INIT;
19691987
struct option *options = NULL;
19701988
struct option switch_options[] = {
19711989
OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
@@ -1979,7 +1997,6 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
19791997
OPT_END()
19801998
};
19811999

1982-
memset(&opts, 0, sizeof(opts));
19832000
opts.dwim_new_local_branch = 1;
19842001
opts.accept_ref = 1;
19852002
opts.accept_pathspec = 0;
@@ -2002,7 +2019,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
20022019

20032020
int cmd_restore(int argc, const char **argv, const char *prefix)
20042021
{
2005-
struct checkout_opts opts;
2022+
struct checkout_opts opts = CHECKOUT_OPTS_INIT;
20062023
struct option *options;
20072024
struct option restore_options[] = {
20082025
OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
@@ -2017,7 +2034,6 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
20172034
OPT_END()
20182035
};
20192036

2020-
memset(&opts, 0, sizeof(opts));
20212037
opts.accept_ref = 0;
20222038
opts.accept_pathspec = 1;
20232039
opts.empty_pathspec_ok = 0;

merge-ll.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ static enum ll_merge_result ll_xdl_merge(const struct ll_merge_driver *drv_unuse
128128
xmp.level = XDL_MERGE_ZEALOUS;
129129
xmp.favor = opts->variant;
130130
xmp.xpp.flags = opts->xdl_opts;
131-
if (git_xmerge_style >= 0)
131+
if (opts->conflict_style >= 0)
132+
xmp.style = opts->conflict_style;
133+
else if (git_xmerge_style >= 0)
132134
xmp.style = git_xmerge_style;
133135
if (marker_size > 0)
134136
xmp.marker_size = marker_size;
@@ -401,7 +403,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
401403
const struct ll_merge_options *opts)
402404
{
403405
struct attr_check *check = load_merge_attributes();
404-
static const struct ll_merge_options default_opts;
406+
static const struct ll_merge_options default_opts = LL_MERGE_OPTIONS_INIT;
405407
const char *ll_driver_name = NULL;
406408
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
407409
const struct ll_merge_driver *driver;

merge-ll.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,15 @@ struct ll_merge_options {
7878
*/
7979
unsigned extra_marker_size;
8080

81+
/* Override the global conflict style. */
82+
int conflict_style;
83+
8184
/* Extra xpparam_t flags as defined in xdiff/xdiff.h. */
8285
long xdl_opts;
8386
};
8487

88+
#define LL_MERGE_OPTIONS_INIT { .conflict_style = -1 }
89+
8590
enum ll_merge_result {
8691
LL_MERGE_ERROR = -1,
8792
LL_MERGE_OK = 0,

merge-ort.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2025,7 +2025,7 @@ static int merge_3way(struct merge_options *opt,
20252025
mmbuffer_t *result_buf)
20262026
{
20272027
mmfile_t orig, src1, src2;
2028-
struct ll_merge_options ll_opts = {0};
2028+
struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
20292029
char *base, *name1, *name2;
20302030
enum ll_merge_result merge_status;
20312031

@@ -2035,6 +2035,7 @@ static int merge_3way(struct merge_options *opt,
20352035
ll_opts.renormalize = opt->renormalize;
20362036
ll_opts.extra_marker_size = extra_marker_size;
20372037
ll_opts.xdl_opts = opt->xdl_opts;
2038+
ll_opts.conflict_style = opt->conflict_style;
20382039

20392040
if (opt->priv->call_depth) {
20402041
ll_opts.virtual_ancestor = 1;

merge-recursive.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,13 +1048,14 @@ static int merge_3way(struct merge_options *opt,
10481048
const int extra_marker_size)
10491049
{
10501050
mmfile_t orig, src1, src2;
1051-
struct ll_merge_options ll_opts = {0};
1051+
struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
10521052
char *base, *name1, *name2;
10531053
enum ll_merge_result merge_status;
10541054

10551055
ll_opts.renormalize = opt->renormalize;
10561056
ll_opts.extra_marker_size = extra_marker_size;
10571057
ll_opts.xdl_opts = opt->xdl_opts;
1058+
ll_opts.conflict_style = opt->conflict_style;
10581059

10591060
if (opt->priv->call_depth) {
10601061
ll_opts.virtual_ancestor = 1;
@@ -3947,6 +3948,8 @@ void init_merge_options(struct merge_options *opt,
39473948

39483949
opt->renormalize = 0;
39493950

3951+
opt->conflict_style = -1;
3952+
39503953
merge_recursive_config(opt);
39513954
merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
39523955
if (merge_verbosity)

merge-recursive.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct merge_options {
3131

3232
/* xdiff-related options (patience, ignore whitespace, ours/theirs) */
3333
long xdl_opts;
34+
int conflict_style;
3435
enum {
3536
MERGE_VARIANT_NORMAL = 0,
3637
MERGE_VARIANT_OURS,

t/t7201-co.sh

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

634+
test_expect_success 'checkout --conflict=diff3 --no-conflict does not merge' '
635+
setup_conflicting_index &&
636+
echo "none of the above" >expect &&
637+
cat expect >fild &&
638+
cat expect >file &&
639+
test_must_fail git checkout --conflict=diff3 --no-conflict -- fild file 2>err &&
640+
test_cmp expect file &&
641+
test_cmp expect fild &&
642+
echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
643+
test_cmp expect err
644+
'
645+
646+
test_expect_success 'checkout --conflict=diff3 --no-merge does not merge' '
647+
setup_conflicting_index &&
648+
echo "none of the above" >expect &&
649+
cat expect >fild &&
650+
cat expect >file &&
651+
test_must_fail git checkout --conflict=diff3 --no-merge -- fild file 2>err &&
652+
test_cmp expect file &&
653+
test_cmp expect fild &&
654+
echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
655+
test_cmp expect err
656+
'
657+
658+
test_expect_success 'checkout --no-merge --conflict=diff3 does merge' '
659+
setup_conflicting_index &&
660+
echo "none of the above" >fild &&
661+
echo "none of the above" >file &&
662+
git checkout --no-merge --conflict=diff3 -- fild file &&
663+
echo "ourside" >expect &&
664+
test_cmp expect fild &&
665+
cat >expect <<-\EOF &&
666+
<<<<<<< ours
667+
ourside
668+
||||||| base
669+
original
670+
=======
671+
theirside
672+
>>>>>>> theirs
673+
EOF
674+
test_cmp expect file
675+
'
676+
677+
test_expect_success 'checkout --merge --conflict=diff3 --no-conflict does merge' '
678+
setup_conflicting_index &&
679+
echo "none of the above" >fild &&
680+
echo "none of the above" >file &&
681+
git checkout --merge --conflict=diff3 --no-conflict -- fild file &&
682+
echo "ourside" >expect &&
683+
test_cmp expect fild &&
684+
cat >expect <<-\EOF &&
685+
<<<<<<< ours
686+
ourside
687+
=======
688+
theirside
689+
>>>>>>> theirs
690+
EOF
691+
test_cmp expect file
692+
'
693+
694+
test_expect_success 'checkout with invalid conflict style' '
695+
test_must_fail git checkout --conflict=bad 2>actual -- file &&
696+
echo "error: unknown conflict style ${SQ}bad${SQ}" >expect &&
697+
test_cmp expect actual
698+
'
699+
634700
test_expect_success 'failing checkout -b should not break working tree' '
635701
git clean -fd && # Remove untracked files in the way
636702
git reset --hard main &&

xdiff-interface.c

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,22 @@ int xdiff_compare_lines(const char *l1, long s1,
305305
return xdl_recmatch(l1, s1, l2, s2, flags);
306306
}
307307

308+
int parse_conflict_style_name(const char *value)
309+
{
310+
if (!strcmp(value, "diff3"))
311+
return XDL_MERGE_DIFF3;
312+
else if (!strcmp(value, "zdiff3"))
313+
return XDL_MERGE_ZEALOUS_DIFF3;
314+
else if (!strcmp(value, "merge"))
315+
return 0;
316+
/*
317+
* Please update _git_checkout() in git-completion.bash when
318+
* you add new merge config
319+
*/
320+
else
321+
return -1;
322+
}
323+
308324
int git_xmerge_style = -1;
309325

310326
int git_xmerge_config(const char *var, const char *value,
@@ -313,17 +329,8 @@ int git_xmerge_config(const char *var, const char *value,
313329
if (!strcmp(var, "merge.conflictstyle")) {
314330
if (!value)
315331
return config_error_nonbool(var);
316-
if (!strcmp(value, "diff3"))
317-
git_xmerge_style = XDL_MERGE_DIFF3;
318-
else if (!strcmp(value, "zdiff3"))
319-
git_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;
320-
else if (!strcmp(value, "merge"))
321-
git_xmerge_style = 0;
322-
/*
323-
* Please update _git_checkout() in
324-
* git-completion.bash when you add new merge config
325-
*/
326-
else
332+
git_xmerge_style = parse_conflict_style_name(value);
333+
if (git_xmerge_style == -1)
327334
return error(_("unknown style '%s' given for '%s'"),
328335
value, var);
329336
return 0;

xdiff-interface.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ int buffer_is_binary(const char *ptr, unsigned long size);
5151
void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
5252
void xdiff_clear_find_func(xdemitconf_t *xecfg);
5353
struct config_context;
54+
int parse_conflict_style_name(const char *value);
5455
int git_xmerge_config(const char *var, const char *value,
5556
const struct config_context *ctx, void *cb);
5657
extern int git_xmerge_style;

0 commit comments

Comments
 (0)