Skip to content

Commit 6571d82

Browse files
Lin Sungitster
authored andcommitted
Support auto-merge for meld to follow the vim-diff behavior
Make the mergetool used with "meld" backend behave similarly to "vimdiff" by telling it to auto-merge non-conflicting parts and highlight the conflicting parts when `mergetool.meld.useAutoMerge` is configured with `true`, or `auto` for detecting the `--auto-merge` option automatically. Helped-by: Đoàn Trần Công Danh <[email protected]> Helped-by: David Aguilar <[email protected]> Signed-off-by: Lin Sun <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a08a83d commit 6571d82

File tree

5 files changed

+115
-16
lines changed

5 files changed

+115
-16
lines changed

Documentation/config/mergetool.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ mergetool.meld.hasOutput::
3030
to `true` tells Git to unconditionally use the `--output` option,
3131
and `false` avoids using `--output`.
3232

33+
mergetool.meld.useAutoMerge::
34+
When the `--auto-merge` is given, meld will merge all non-conflicting
35+
parts automatically, highlight the conflicting parts and wait for
36+
user decision. Setting `mergetool.meld.useAutoMerge` to `true` tells
37+
Git to unconditionally use the `--auto-merge` option with `meld`.
38+
Setting this value to `auto` makes git detect whether `--auto-merge`
39+
is supported and will only use `--auto-merge` when available. A
40+
value of `false` avoids using `--auto-merge` altogether, and is the
41+
default value.
42+
3343
mergetool.keepBackup::
3444
After performing a merge, the original file with conflict markers
3545
can be saved as a file with a `.orig` extension. If this variable

builtin/config.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ static int show_scope;
6565
#define TYPE_PATH 4
6666
#define TYPE_EXPIRY_DATE 5
6767
#define TYPE_COLOR 6
68+
#define TYPE_BOOL_OR_STR 7
6869

6970
#define OPT_CALLBACK_VALUE(s, l, v, h, i) \
7071
{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
@@ -94,6 +95,8 @@ static int option_parse_type(const struct option *opt, const char *arg,
9495
new_type = TYPE_INT;
9596
else if (!strcmp(arg, "bool-or-int"))
9697
new_type = TYPE_BOOL_OR_INT;
98+
else if (!strcmp(arg, "bool-or-str"))
99+
new_type = TYPE_BOOL_OR_STR;
97100
else if (!strcmp(arg, "path"))
98101
new_type = TYPE_PATH;
99102
else if (!strcmp(arg, "expiry-date"))
@@ -149,6 +152,7 @@ static struct option builtin_config_options[] = {
149152
OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
150153
OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
151154
OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
155+
OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
152156
OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
153157
OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
154158
OPT_GROUP(N_("Other")),
@@ -250,6 +254,13 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
250254
strbuf_addstr(buf, v ? "true" : "false");
251255
else
252256
strbuf_addf(buf, "%d", v);
257+
} else if (type == TYPE_BOOL_OR_STR) {
258+
int is_bool, v;
259+
v = git_config_bool_or_str(NULL, key_, value_, &is_bool);
260+
if (is_bool)
261+
strbuf_addstr(buf, v ? "true" : "false");
262+
else
263+
strbuf_addstr(buf, value_);
253264
} else if (type == TYPE_PATH) {
254265
const char *v;
255266
if (git_config_pathname(&v, key_, value_) < 0)
@@ -411,6 +422,14 @@ static char *normalize_value(const char *key, const char *value)
411422
else
412423
return xstrdup(v ? "true" : "false");
413424
}
425+
if (type == TYPE_BOOL_OR_STR) {
426+
int is_bool, v;
427+
v = git_config_bool_or_str(NULL, key, value, &is_bool);
428+
if (!is_bool)
429+
return xstrdup(value);
430+
else
431+
return xstrdup(v ? "true" : "false");
432+
}
414433
if (type == TYPE_COLOR) {
415434
char v[COLOR_MAXLEN];
416435
if (git_config_color(v, key, value))

config.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,20 @@ int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
11001100
return git_config_int(name, value);
11011101
}
11021102

1103+
int git_config_bool_or_str(const char **dest, const char *name, const char *value, int *is_bool)
1104+
{
1105+
int v = git_parse_maybe_bool_text(value);
1106+
if (0 <= v) {
1107+
*is_bool = 1;
1108+
return v;
1109+
}
1110+
*is_bool = 0;
1111+
if (dest != NULL)
1112+
return git_config_string(dest, name, value);
1113+
else
1114+
return 0;
1115+
}
1116+
11031117
int git_config_bool(const char *name, const char *value)
11041118
{
11051119
int discard;

config.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,13 @@ ssize_t git_config_ssize_t(const char *, const char *);
217217
*/
218218
int git_config_bool_or_int(const char *, const char *, int *);
219219

220+
/**
221+
* Same as `git_config_bool`, except that `is_bool` flag is unset, then if
222+
* `dest` parameter is non-NULL, it allocates and copies the value string
223+
* into the `dest`, if `dest` is NULL and `is_bool` flag is unset it return 0.
224+
*/
225+
int git_config_bool_or_str(const char **, const char *, const char *, int *);
226+
220227
/**
221228
* Parse a string into a boolean value, respecting keywords like "true" and
222229
* "false". Integer values are converted into true/false values (when they

mergetools/meld

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,83 @@ diff_cmd () {
33
}
44

55
merge_cmd () {
6-
if test -z "${meld_has_output_option:+set}"
6+
check_meld_for_features
7+
8+
option_auto_merge=
9+
if test "$meld_use_auto_merge_option" = true
710
then
8-
check_meld_for_output_version
11+
option_auto_merge="--auto-merge"
912
fi
1013

1114
if test "$meld_has_output_option" = true
1215
then
13-
"$merge_tool_path" --output="$MERGED" \
16+
"$merge_tool_path" $option_auto_merge --output="$MERGED" \
1417
"$LOCAL" "$BASE" "$REMOTE"
1518
else
16-
"$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
19+
"$merge_tool_path" $option_auto_merge "$LOCAL" "$MERGED" "$REMOTE"
1720
fi
1821
}
1922

20-
# Check whether we should use 'meld --output <file>'
21-
check_meld_for_output_version () {
22-
meld_path="$(git config mergetool.meld.path)"
23-
meld_path="${meld_path:-meld}"
23+
# Get meld help message
24+
init_meld_help_msg () {
25+
if test -z "$meld_help_msg"
26+
then
27+
meld_path="$(git config mergetool.meld.path || echo meld)"
28+
meld_help_msg=$("$meld_path" --help 2>&1)
29+
fi
30+
}
2431

25-
if meld_has_output_option=$(git config --bool mergetool.meld.hasOutput)
32+
# Check the features and set flags
33+
check_meld_for_features () {
34+
# Check whether we should use 'meld --output <file>'
35+
if test -z "$meld_has_output_option"
2636
then
27-
: use configured value
28-
elif "$meld_path" --help 2>&1 |
29-
grep -e '--output=' -e '\[OPTION\.\.\.\]' >/dev/null
37+
meld_has_output_option=$(git config --bool mergetool.meld.hasOutput)
38+
case "$meld_has_output_option" in
39+
true|false)
40+
: use configured value
41+
;;
42+
*)
43+
: empty or invalid configured value, detecting "--output" automatically
44+
init_meld_help_msg
45+
46+
case "$meld_help_msg" in
47+
*"--output="*|*'[OPTION...]'*)
48+
# All version that has [OPTION...] supports --output
49+
meld_has_output_option=true
50+
;;
51+
*)
52+
meld_has_output_option=false
53+
;;
54+
esac
55+
;;
56+
esac
57+
fi
58+
# Check whether we should use 'meld --auto-merge ...'
59+
if test -z "$meld_use_auto_merge_option"
3060
then
31-
: old ones mention --output and new ones just say OPTION...
32-
meld_has_output_option=true
33-
else
34-
meld_has_output_option=false
61+
meld_use_auto_merge_option=$(
62+
git config --bool-or-str mergetool.meld.useAutoMerge)
63+
case "$meld_use_auto_merge_option" in
64+
true|false)
65+
: use well formatted boolean value
66+
;;
67+
auto)
68+
# testing the "--auto-merge" option only if config is "auto"
69+
init_meld_help_msg
70+
71+
case "$meld_help_msg" in
72+
*"--auto-merge"*|*'[OPTION...]'*)
73+
meld_use_auto_merge_option=true
74+
;;
75+
*)
76+
meld_use_auto_merge_option=false
77+
;;
78+
esac
79+
;;
80+
*)
81+
meld_use_auto_merge_option=false
82+
;;
83+
esac
3584
fi
3685
}

0 commit comments

Comments
 (0)