Skip to content

Commit d280170

Browse files
committed
Merge branch 'hn/highlight-sideband-keywords'
The sideband code learned to optionally paint selected keywords at the beginning of incoming lines on the receiving end. * hn/highlight-sideband-keywords: sideband: do not read beyond the end of input sideband: highlight keywords in remote sideband output
2 parents 39e415c + 59a255a commit d280170

File tree

5 files changed

+235
-9
lines changed

5 files changed

+235
-9
lines changed

Documentation/config.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,18 @@ color.push::
12971297
color.push.error::
12981298
Use customized color for push errors.
12991299

1300+
color.remote::
1301+
If set, keywords at the start of the line are highlighted. The
1302+
keywords are "error", "warning", "hint" and "success", and are
1303+
matched case-insensitively. May be set to `always`, `false` (or
1304+
`never`) or `auto` (or `true`). If unset, then the value of
1305+
`color.ui` is used (`auto` by default).
1306+
1307+
color.remote.<slot>::
1308+
Use customized color for each remote keyword. `<slot>` may be
1309+
`hint`, `warning`, `success` or `error` which match the
1310+
corresponding keyword.
1311+
13001312
color.showBranch::
13011313
A boolean to enable/disable color in the output of
13021314
linkgit:git-show-branch[1]. May be set to `always`,

help.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ void list_config_help(int for_human)
425425
{ "color.diff", "<slot>", list_config_color_diff_slots },
426426
{ "color.grep", "<slot>", list_config_color_grep_slots },
427427
{ "color.interactive", "<slot>", list_config_color_interactive_slots },
428+
{ "color.remote", "<slot>", list_config_color_sideband_slots },
428429
{ "color.status", "<slot>", list_config_color_status_slots },
429430
{ "fsck", "<msg-id>", list_config_fsck_msg_ids },
430431
{ "receive.fsck", "<msg-id>", list_config_fsck_msg_ids },

help.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ void list_config_color_diff_slots(struct string_list *list, const char *prefix);
8383
void list_config_color_grep_slots(struct string_list *list, const char *prefix);
8484
void list_config_color_interactive_slots(struct string_list *list, const char *prefix);
8585
void list_config_color_status_slots(struct string_list *list, const char *prefix);
86+
void list_config_color_sideband_slots(struct string_list *list, const char *prefix);
8687
void list_config_fsck_msg_ids(struct string_list *list, const char *prefix);
8788

8889
#endif /* HELP_H */

sideband.c

Lines changed: 120 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,113 @@
11
#include "cache.h"
2+
#include "color.h"
3+
#include "config.h"
24
#include "pkt-line.h"
35
#include "sideband.h"
6+
#include "help.h"
7+
8+
struct keyword_entry {
9+
/*
10+
* We use keyword as config key so it should be a single alphanumeric word.
11+
*/
12+
const char *keyword;
13+
char color[COLOR_MAXLEN];
14+
};
15+
16+
static struct keyword_entry keywords[] = {
17+
{ "hint", GIT_COLOR_YELLOW },
18+
{ "warning", GIT_COLOR_BOLD_YELLOW },
19+
{ "success", GIT_COLOR_BOLD_GREEN },
20+
{ "error", GIT_COLOR_BOLD_RED },
21+
};
22+
23+
/* Returns a color setting (GIT_COLOR_NEVER, etc). */
24+
static int use_sideband_colors(void)
25+
{
26+
static int use_sideband_colors_cached = -1;
27+
28+
const char *key = "color.remote";
29+
struct strbuf sb = STRBUF_INIT;
30+
char *value;
31+
int i;
32+
33+
if (use_sideband_colors_cached >= 0)
34+
return use_sideband_colors_cached;
35+
36+
if (!git_config_get_string(key, &value)) {
37+
use_sideband_colors_cached = git_config_colorbool(key, value);
38+
} else if (!git_config_get_string("color.ui", &value)) {
39+
use_sideband_colors_cached = git_config_colorbool("color.ui", value);
40+
} else {
41+
use_sideband_colors_cached = GIT_COLOR_AUTO;
42+
}
43+
44+
for (i = 0; i < ARRAY_SIZE(keywords); i++) {
45+
strbuf_reset(&sb);
46+
strbuf_addf(&sb, "%s.%s", key, keywords[i].keyword);
47+
if (git_config_get_string(sb.buf, &value))
48+
continue;
49+
if (color_parse(value, keywords[i].color))
50+
continue;
51+
}
52+
strbuf_release(&sb);
53+
return use_sideband_colors_cached;
54+
}
55+
56+
void list_config_color_sideband_slots(struct string_list *list, const char *prefix)
57+
{
58+
int i;
59+
60+
for (i = 0; i < ARRAY_SIZE(keywords); i++)
61+
list_config_item(list, prefix, keywords[i].keyword);
62+
}
63+
64+
/*
65+
* Optionally highlight one keyword in remote output if it appears at the start
66+
* of the line. This should be called for a single line only, which is
67+
* passed as the first N characters of the SRC array.
68+
*
69+
* NEEDSWORK: use "size_t n" instead for clarity.
70+
*/
71+
static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
72+
{
73+
int i;
74+
75+
if (!want_color_stderr(use_sideband_colors())) {
76+
strbuf_add(dest, src, n);
77+
return;
78+
}
79+
80+
while (0 < n && isspace(*src)) {
81+
strbuf_addch(dest, *src);
82+
src++;
83+
n--;
84+
}
85+
86+
for (i = 0; i < ARRAY_SIZE(keywords); i++) {
87+
struct keyword_entry *p = keywords + i;
88+
int len = strlen(p->keyword);
89+
90+
if (n <= len)
91+
continue;
92+
/*
93+
* Match case insensitively, so we colorize output from existing
94+
* servers regardless of the case that they use for their
95+
* messages. We only highlight the word precisely, so
96+
* "successful" stays uncolored.
97+
*/
98+
if (!strncasecmp(p->keyword, src, len) && !isalnum(src[len])) {
99+
strbuf_addstr(dest, p->color);
100+
strbuf_add(dest, src, len);
101+
strbuf_addstr(dest, GIT_COLOR_RESET);
102+
n -= len;
103+
src += len;
104+
break;
105+
}
106+
}
107+
108+
strbuf_add(dest, src, n);
109+
}
110+
4111

5112
/*
6113
* Receive multiplexed output stream over git native protocol.
@@ -48,8 +155,10 @@ int recv_sideband(const char *me, int in_stream, int out)
48155
len--;
49156
switch (band) {
50157
case 3:
51-
strbuf_addf(&outbuf, "%s%s%s", outbuf.len ? "\n" : "",
52-
DISPLAY_PREFIX, buf + 1);
158+
strbuf_addf(&outbuf, "%s%s", outbuf.len ? "\n" : "",
159+
DISPLAY_PREFIX);
160+
maybe_colorize_sideband(&outbuf, buf + 1, len);
161+
53162
retval = SIDEBAND_REMOTE_ERROR;
54163
break;
55164
case 2:
@@ -69,20 +178,22 @@ int recv_sideband(const char *me, int in_stream, int out)
69178
if (!outbuf.len)
70179
strbuf_addstr(&outbuf, DISPLAY_PREFIX);
71180
if (linelen > 0) {
72-
strbuf_addf(&outbuf, "%.*s%s%c",
73-
linelen, b, suffix, *brk);
74-
} else {
75-
strbuf_addch(&outbuf, *brk);
181+
maybe_colorize_sideband(&outbuf, b, linelen);
182+
strbuf_addstr(&outbuf, suffix);
76183
}
184+
185+
strbuf_addch(&outbuf, *brk);
77186
xwrite(2, outbuf.buf, outbuf.len);
78187
strbuf_reset(&outbuf);
79188

80189
b = brk + 1;
81190
}
82191

83-
if (*b)
84-
strbuf_addf(&outbuf, "%s%s", outbuf.len ?
85-
"" : DISPLAY_PREFIX, b);
192+
if (*b) {
193+
strbuf_addstr(&outbuf, outbuf.len ?
194+
"" : DISPLAY_PREFIX);
195+
maybe_colorize_sideband(&outbuf, b, strlen(b));
196+
}
86197
break;
87198
case 1:
88199
write_or_die(out, buf + 1, len);

t/t5409-colorize-remote-messages.sh

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#!/bin/sh
2+
3+
test_description='remote messages are colorized on the client'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'setup' '
8+
mkdir .git/hooks &&
9+
write_script .git/hooks/update <<-\EOF &&
10+
echo error: error
11+
echo ERROR: also highlighted
12+
echo hint: hint
13+
echo hinting: not highlighted
14+
echo success: success
15+
echo warning: warning
16+
echo prefixerror: error
17+
echo " " "error: leading space"
18+
echo " "
19+
echo Err
20+
exit 0
21+
EOF
22+
echo 1 >file &&
23+
git add file &&
24+
git commit -m 1 &&
25+
git clone . child &&
26+
(
27+
cd child &&
28+
test_commit message2 file content2
29+
)
30+
'
31+
32+
test_expect_success 'keywords' '
33+
git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/keywords 2>output &&
34+
test_decode_color <output >decoded &&
35+
grep "<BOLD;RED>error<RESET>: error" decoded &&
36+
grep "<YELLOW>hint<RESET>:" decoded &&
37+
grep "<BOLD;GREEN>success<RESET>:" decoded &&
38+
grep "<BOLD;YELLOW>warning<RESET>:" decoded
39+
'
40+
41+
test_expect_success 'whole words at line start' '
42+
git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/whole-words 2>output &&
43+
test_decode_color <output >decoded &&
44+
grep "<YELLOW>hint<RESET>:" decoded &&
45+
grep "hinting: not highlighted" decoded &&
46+
grep "prefixerror: error" decoded
47+
'
48+
49+
test_expect_success 'short line' '
50+
git -C child -c color.remote=always push -f origin HEAD:short-line 2>output &&
51+
test_decode_color <output >decoded &&
52+
grep "remote: Err" decoded
53+
'
54+
55+
test_expect_success 'case-insensitive' '
56+
git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/case-insensitive 2>output &&
57+
cat output &&
58+
test_decode_color <output >decoded &&
59+
grep "<BOLD;RED>error<RESET>: error" decoded &&
60+
grep "<BOLD;RED>ERROR<RESET>: also highlighted" decoded
61+
'
62+
63+
test_expect_success 'leading space' '
64+
git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/leading-space 2>output && cat output &&
65+
test_decode_color <output >decoded &&
66+
grep " <BOLD;RED>error<RESET>: leading space" decoded
67+
'
68+
69+
test_expect_success 'spaces only' '
70+
git -C child -c color.remote=always push -f origin HEAD:only-space 2>output &&
71+
test_decode_color <output >decoded &&
72+
grep "remote: " decoded
73+
'
74+
75+
test_expect_success 'no coloring for redirected output' '
76+
git --git-dir child/.git push -f origin HEAD:refs/heads/redirected-output 2>output &&
77+
test_decode_color <output >decoded &&
78+
grep "error: error" decoded
79+
'
80+
81+
test_expect_success 'push with customized color' '
82+
git --git-dir child/.git -c color.remote=always -c color.remote.error=blue push -f origin HEAD:refs/heads/customized-color 2>output &&
83+
test_decode_color <output >decoded &&
84+
grep "<BLUE>error<RESET>:" decoded &&
85+
grep "<BOLD;GREEN>success<RESET>:" decoded
86+
'
87+
88+
89+
test_expect_success 'error in customized color' '
90+
git --git-dir child/.git -c color.remote=always -c color.remote.error=i-am-not-a-color push -f origin HEAD:refs/heads/error-customized-color 2>output &&
91+
test_decode_color <output >decoded &&
92+
grep "<BOLD;GREEN>success<RESET>:" decoded
93+
'
94+
95+
test_expect_success 'fallback to color.ui' '
96+
git --git-dir child/.git -c color.ui=always push -f origin HEAD:refs/heads/fallback-color-ui 2>output &&
97+
test_decode_color <output >decoded &&
98+
grep "<BOLD;RED>error<RESET>: error" decoded
99+
'
100+
101+
test_done

0 commit comments

Comments
 (0)