From a73b3680c4910866e3e215c3927a0c71f0b9229d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:21 +0200 Subject: [PATCH 01/12] Add and use generic name->id mapping code for color slot parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of hard coding the name-to-id mapping in C code, keep it in an array and use a common function to do the parsing. This reduces code and also allows us to list all possible color slots later. This starts using C99 designated initializers more for convenience (the first designated initializers have been introduced in builtin/clean.c for some time without complaints) Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/branch.c | 28 +++++++++---------------- builtin/clean.c | 28 +++++++++---------------- builtin/commit.c | 33 ++++++++++++++---------------- config.c | 13 ++++++++++++ config.h | 4 ++++ diff.c | 53 +++++++++++++++++++----------------------------- log-tree.c | 35 ++++++++------------------------ 7 files changed, 82 insertions(+), 112 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index efc9ac1922..136d57caa4 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -55,26 +55,18 @@ enum color_branch { BRANCH_COLOR_UPSTREAM = 5 }; +static const char *color_branch_slots[] = { + [BRANCH_COLOR_RESET] = "reset", + [BRANCH_COLOR_PLAIN] = "plain", + [BRANCH_COLOR_REMOTE] = "remote", + [BRANCH_COLOR_LOCAL] = "local", + [BRANCH_COLOR_CURRENT] = "current", + [BRANCH_COLOR_UPSTREAM] = "upstream", +}; + static struct string_list output = STRING_LIST_INIT_DUP; static unsigned int colopts; -static int parse_branch_color_slot(const char *slot) -{ - if (!strcasecmp(slot, "plain")) - return BRANCH_COLOR_PLAIN; - if (!strcasecmp(slot, "reset")) - return BRANCH_COLOR_RESET; - if (!strcasecmp(slot, "remote")) - return BRANCH_COLOR_REMOTE; - if (!strcasecmp(slot, "local")) - return BRANCH_COLOR_LOCAL; - if (!strcasecmp(slot, "current")) - return BRANCH_COLOR_CURRENT; - if (!strcasecmp(slot, "upstream")) - return BRANCH_COLOR_UPSTREAM; - return -1; -} - static int git_branch_config(const char *var, const char *value, void *cb) { const char *slot_name; @@ -86,7 +78,7 @@ static int git_branch_config(const char *var, const char *value, void *cb) return 0; } if (skip_prefix(var, "color.branch.", &slot_name)) { - int slot = parse_branch_color_slot(slot_name); + int slot = LOOKUP_CONFIG(color_branch_slots, slot_name); if (slot < 0) return 0; if (!value) diff --git a/builtin/clean.c b/builtin/clean.c index fad533a0a7..0ccd95e693 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -42,6 +42,15 @@ enum color_clean { CLEAN_COLOR_ERROR = 5 }; +static const char *color_interactive_slots[] = { + [CLEAN_COLOR_ERROR] = "error", + [CLEAN_COLOR_HEADER] = "header", + [CLEAN_COLOR_HELP] = "help", + [CLEAN_COLOR_PLAIN] = "plain", + [CLEAN_COLOR_PROMPT] = "prompt", + [CLEAN_COLOR_RESET] = "reset", +}; + static int clean_use_color = -1; static char clean_colors[][COLOR_MAXLEN] = { [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED, @@ -82,23 +91,6 @@ struct menu_stuff { void *stuff; }; -static int parse_clean_color_slot(const char *var) -{ - if (!strcasecmp(var, "reset")) - return CLEAN_COLOR_RESET; - if (!strcasecmp(var, "plain")) - return CLEAN_COLOR_PLAIN; - if (!strcasecmp(var, "prompt")) - return CLEAN_COLOR_PROMPT; - if (!strcasecmp(var, "header")) - return CLEAN_COLOR_HEADER; - if (!strcasecmp(var, "help")) - return CLEAN_COLOR_HELP; - if (!strcasecmp(var, "error")) - return CLEAN_COLOR_ERROR; - return -1; -} - static int git_clean_config(const char *var, const char *value, void *cb) { const char *slot_name; @@ -113,7 +105,7 @@ static int git_clean_config(const char *var, const char *value, void *cb) return 0; } if (skip_prefix(var, "color.interactive.", &slot_name)) { - int slot = parse_clean_color_slot(slot_name); + int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name); if (slot < 0) return 0; if (!value) diff --git a/builtin/commit.c b/builtin/commit.c index 5240f11225..f96755aa4b 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -66,6 +66,18 @@ N_("If you wish to skip this commit, use:\n" "Then \"git cherry-pick --continue\" will resume cherry-picking\n" "the remaining commits.\n"); +static const char *color_status_slots[] = { + [WT_STATUS_HEADER] = "header", + [WT_STATUS_UPDATED] = "updated", + [WT_STATUS_CHANGED] = "changed", + [WT_STATUS_UNTRACKED] = "untracked", + [WT_STATUS_NOBRANCH] = "noBranch", + [WT_STATUS_UNMERGED] = "unmerged", + [WT_STATUS_LOCAL_BRANCH] = "localBranch", + [WT_STATUS_REMOTE_BRANCH] = "remoteBranch", + [WT_STATUS_ONBRANCH] = "branch", +}; + static const char *use_message_buffer; static struct lock_file index_lock; /* real index */ static struct lock_file false_lock; /* used only for partial commits */ @@ -1175,25 +1187,10 @@ static int dry_run_commit(int argc, const char **argv, const char *prefix, static int parse_status_slot(const char *slot) { - if (!strcasecmp(slot, "header")) - return WT_STATUS_HEADER; - if (!strcasecmp(slot, "branch")) - return WT_STATUS_ONBRANCH; - if (!strcasecmp(slot, "updated") || !strcasecmp(slot, "added")) + if (!strcasecmp(slot, "added")) return WT_STATUS_UPDATED; - if (!strcasecmp(slot, "changed")) - return WT_STATUS_CHANGED; - if (!strcasecmp(slot, "untracked")) - return WT_STATUS_UNTRACKED; - if (!strcasecmp(slot, "nobranch")) - return WT_STATUS_NOBRANCH; - if (!strcasecmp(slot, "unmerged")) - return WT_STATUS_UNMERGED; - if (!strcasecmp(slot, "localBranch")) - return WT_STATUS_LOCAL_BRANCH; - if (!strcasecmp(slot, "remoteBranch")) - return WT_STATUS_REMOTE_BRANCH; - return -1; + + return LOOKUP_CONFIG(color_status_slots, slot); } static int git_status_config(const char *k, const char *v, void *cb) diff --git a/config.c b/config.c index 6f8f1d8c11..d9ff3cb38c 100644 --- a/config.c +++ b/config.c @@ -3245,3 +3245,16 @@ enum config_scope current_config_scope(void) else return current_parsing_scope; } + +int lookup_config(const char **mapping, int nr_mapping, const char *var) +{ + int i; + + for (i = 0; i < nr_mapping; i++) { + const char *name = mapping[i]; + + if (name && !strcasecmp(var, name)) + return i; + } + return -1; +} diff --git a/config.h b/config.h index cdac2fc73e..626d4654bd 100644 --- a/config.h +++ b/config.h @@ -257,4 +257,8 @@ struct key_value_info { extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3))); extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr); +#define LOOKUP_CONFIG(mapping, var) \ + lookup_config(mapping, ARRAY_SIZE(mapping), var) +int lookup_config(const char **mapping, int nr_mapping, const char *var); + #endif /* CONFIG_H */ diff --git a/diff.c b/diff.c index 4753170fe1..56d7bfded6 100644 --- a/diff.c +++ b/diff.c @@ -69,6 +69,25 @@ static char diff_colors[][COLOR_MAXLEN] = { GIT_COLOR_FAINT_ITALIC, /* NEW_MOVED_ALTERNATIVE_DIM */ }; +static const char *color_diff_slots[] = { + [DIFF_CONTEXT] = "context", + [DIFF_METAINFO] = "meta", + [DIFF_FRAGINFO] = "frag", + [DIFF_FILE_OLD] = "old", + [DIFF_FILE_NEW] = "new", + [DIFF_COMMIT] = "commit", + [DIFF_WHITESPACE] = "whitespace", + [DIFF_FUNCINFO] = "func", + [DIFF_FILE_OLD_MOVED] = "oldMoved", + [DIFF_FILE_OLD_MOVED_ALT] = "oldMovedAlternative", + [DIFF_FILE_OLD_MOVED_DIM] = "oldMovedDimmed", + [DIFF_FILE_OLD_MOVED_ALT_DIM] = "oldMovedAlternativeDimmed", + [DIFF_FILE_NEW_MOVED] = "newMoved", + [DIFF_FILE_NEW_MOVED_ALT] = "newMovedAlternative", + [DIFF_FILE_NEW_MOVED_DIM] = "newMovedDimmed", + [DIFF_FILE_NEW_MOVED_ALT_DIM] = "newMovedAlternativeDimmed", +}; + static NORETURN void die_want_option(const char *option_name) { die(_("option '%s' requires a value"), option_name); @@ -76,39 +95,9 @@ static NORETURN void die_want_option(const char *option_name) static int parse_diff_color_slot(const char *var) { - if (!strcasecmp(var, "context") || !strcasecmp(var, "plain")) + if (!strcasecmp(var, "plain")) return DIFF_CONTEXT; - if (!strcasecmp(var, "meta")) - return DIFF_METAINFO; - if (!strcasecmp(var, "frag")) - return DIFF_FRAGINFO; - if (!strcasecmp(var, "old")) - return DIFF_FILE_OLD; - if (!strcasecmp(var, "new")) - return DIFF_FILE_NEW; - if (!strcasecmp(var, "commit")) - return DIFF_COMMIT; - if (!strcasecmp(var, "whitespace")) - return DIFF_WHITESPACE; - if (!strcasecmp(var, "func")) - return DIFF_FUNCINFO; - if (!strcasecmp(var, "oldmoved")) - return DIFF_FILE_OLD_MOVED; - if (!strcasecmp(var, "oldmovedalternative")) - return DIFF_FILE_OLD_MOVED_ALT; - if (!strcasecmp(var, "oldmoveddimmed")) - return DIFF_FILE_OLD_MOVED_DIM; - if (!strcasecmp(var, "oldmovedalternativedimmed")) - return DIFF_FILE_OLD_MOVED_ALT_DIM; - if (!strcasecmp(var, "newmoved")) - return DIFF_FILE_NEW_MOVED; - if (!strcasecmp(var, "newmovedalternative")) - return DIFF_FILE_NEW_MOVED_ALT; - if (!strcasecmp(var, "newmoveddimmed")) - return DIFF_FILE_NEW_MOVED_DIM; - if (!strcasecmp(var, "newmovedalternativedimmed")) - return DIFF_FILE_NEW_MOVED_ALT_DIM; - return -1; + return LOOKUP_CONFIG(color_diff_slots, var); } static int parse_dirstat_params(struct diff_options *options, const char *params_string, diff --git a/log-tree.c b/log-tree.c index 724bae0de2..d85e179077 100644 --- a/log-tree.c +++ b/log-tree.c @@ -27,6 +27,14 @@ static char decoration_colors[][COLOR_MAXLEN] = { GIT_COLOR_BOLD_BLUE, /* GRAFTED */ }; +static const char *color_decorate_slots[] = { + [DECORATION_REF_LOCAL] = "branch", + [DECORATION_REF_REMOTE] = "remoteBranch", + [DECORATION_REF_TAG] = "tag", + [DECORATION_REF_STASH] = "stash", + [DECORATION_REF_HEAD] = "HEAD", +}; + static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix) { if (want_color(decorate_use_color)) @@ -34,34 +42,9 @@ static const char *decorate_get_color(int decorate_use_color, enum decoration_ty return ""; } -static int parse_decorate_color_slot(const char *slot) -{ - /* - * We're comparing with 'ignore-case' on - * (because config.c sets them all tolower), - * but let's match the letters in the literal - * string values here with how they are - * documented in Documentation/config.txt, for - * consistency. - * - * We love being consistent, don't we? - */ - if (!strcasecmp(slot, "branch")) - return DECORATION_REF_LOCAL; - if (!strcasecmp(slot, "remoteBranch")) - return DECORATION_REF_REMOTE; - if (!strcasecmp(slot, "tag")) - return DECORATION_REF_TAG; - if (!strcasecmp(slot, "stash")) - return DECORATION_REF_STASH; - if (!strcasecmp(slot, "HEAD")) - return DECORATION_REF_HEAD; - return -1; -} - int parse_decorate_color_config(const char *var, const char *slot_name, const char *value) { - int slot = parse_decorate_color_slot(slot_name); + int slot = LOOKUP_CONFIG(color_decorate_slots, slot_name); if (slot < 0) return 0; if (!value) From fa151dc54dcf7ff32727d5b6fdf78093185ff1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:22 +0200 Subject: [PATCH 02/12] grep: keep all colors in an array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is more inline with how we handle color slots in other code. It also allows us to get the list of configurable color slots later. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- grep.c | 106 ++++++++++++++++++++++++++------------------------------- grep.h | 21 +++++++----- 2 files changed, 62 insertions(+), 65 deletions(-) diff --git a/grep.c b/grep.c index 65b90c10a3..b7012f40f5 100644 --- a/grep.c +++ b/grep.c @@ -13,6 +13,17 @@ static int grep_source_is_binary(struct grep_source *gs); static struct grep_opt grep_defaults; +static const char *color_grep_slots[] = { + [GREP_COLOR_CONTEXT] = "context", + [GREP_COLOR_FILENAME] = "filename", + [GREP_COLOR_FUNCTION] = "function", + [GREP_COLOR_LINENO] = "lineNumber", + [GREP_COLOR_MATCH_CONTEXT] = "matchContext", + [GREP_COLOR_MATCH_SELECTED] = "matchSelected", + [GREP_COLOR_SELECTED] = "selected", + [GREP_COLOR_SEP] = "separator", +}; + static void std_output(struct grep_opt *opt, const void *buf, size_t size) { fwrite(buf, size, 1, stdout); @@ -42,14 +53,14 @@ void init_grep_defaults(void) opt->pathname = 1; opt->max_depth = -1; opt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED; - color_set(opt->color_context, ""); - color_set(opt->color_filename, ""); - color_set(opt->color_function, ""); - color_set(opt->color_lineno, ""); - color_set(opt->color_match_context, GIT_COLOR_BOLD_RED); - color_set(opt->color_match_selected, GIT_COLOR_BOLD_RED); - color_set(opt->color_selected, ""); - color_set(opt->color_sep, GIT_COLOR_CYAN); + color_set(opt->colors[GREP_COLOR_CONTEXT], ""); + color_set(opt->colors[GREP_COLOR_FILENAME], ""); + color_set(opt->colors[GREP_COLOR_FUNCTION], ""); + color_set(opt->colors[GREP_COLOR_LINENO], ""); + color_set(opt->colors[GREP_COLOR_MATCH_CONTEXT], GIT_COLOR_BOLD_RED); + color_set(opt->colors[GREP_COLOR_MATCH_SELECTED], GIT_COLOR_BOLD_RED); + color_set(opt->colors[GREP_COLOR_SELECTED], ""); + color_set(opt->colors[GREP_COLOR_SEP], GIT_COLOR_CYAN); opt->color = -1; opt->output = std_output; } @@ -76,7 +87,7 @@ static int parse_pattern_type_arg(const char *opt, const char *arg) int grep_config(const char *var, const char *value, void *cb) { struct grep_opt *opt = &grep_defaults; - char *color = NULL; + const char *slot; if (userdiff_config(var, value) < 0) return -1; @@ -103,32 +114,18 @@ int grep_config(const char *var, const char *value, void *cb) if (!strcmp(var, "color.grep")) opt->color = git_config_colorbool(var, value); - else if (!strcmp(var, "color.grep.context")) - color = opt->color_context; - else if (!strcmp(var, "color.grep.filename")) - color = opt->color_filename; - else if (!strcmp(var, "color.grep.function")) - color = opt->color_function; - else if (!strcmp(var, "color.grep.linenumber")) - color = opt->color_lineno; - else if (!strcmp(var, "color.grep.matchcontext")) - color = opt->color_match_context; - else if (!strcmp(var, "color.grep.matchselected")) - color = opt->color_match_selected; - else if (!strcmp(var, "color.grep.selected")) - color = opt->color_selected; - else if (!strcmp(var, "color.grep.separator")) - color = opt->color_sep; - else if (!strcmp(var, "color.grep.match")) { - int rc = 0; - if (!value) - return config_error_nonbool(var); - rc |= color_parse(value, opt->color_match_context); - rc |= color_parse(value, opt->color_match_selected); - return rc; - } - - if (color) { + if (!strcmp(var, "color.grep.match")) { + if (grep_config("color.grep.matchcontext", value, cb) < 0) + return -1; + if (grep_config("color.grep.matchselected", value, cb) < 0) + return -1; + } else if (skip_prefix(var, "color.grep.", &slot)) { + int i = LOOKUP_CONFIG(color_grep_slots, slot); + char *color; + + if (i < 0) + return -1; + color = opt->colors[i]; if (!value) return config_error_nonbool(var); return color_parse(value, color); @@ -144,6 +141,7 @@ int grep_config(const char *var, const char *value, void *cb) void grep_init(struct grep_opt *opt, const char *prefix) { struct grep_opt *def = &grep_defaults; + int i; memset(opt, 0, sizeof(*opt)); opt->prefix = prefix; @@ -160,14 +158,8 @@ void grep_init(struct grep_opt *opt, const char *prefix) opt->relative = def->relative; opt->output = def->output; - color_set(opt->color_context, def->color_context); - color_set(opt->color_filename, def->color_filename); - color_set(opt->color_function, def->color_function); - color_set(opt->color_lineno, def->color_lineno); - color_set(opt->color_match_context, def->color_match_context); - color_set(opt->color_match_selected, def->color_match_selected); - color_set(opt->color_selected, def->color_selected); - color_set(opt->color_sep, def->color_sep); + for (i = 0; i < NR_GREP_COLORS; i++) + color_set(opt->colors[i], def->colors[i]); } static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt) @@ -1100,12 +1092,12 @@ static void output_sep(struct grep_opt *opt, char sign) if (opt->null_following_name) opt->output(opt, "\0", 1); else - output_color(opt, &sign, 1, opt->color_sep); + output_color(opt, &sign, 1, opt->colors[GREP_COLOR_SEP]); } static void show_name(struct grep_opt *opt, const char *name) { - output_color(opt, name, strlen(name), opt->color_filename); + output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]); opt->output(opt, opt->null_following_name ? "\0" : "\n", 1); } @@ -1372,28 +1364,28 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, } else if (opt->pre_context || opt->post_context || opt->funcbody) { if (opt->last_shown == 0) { if (opt->show_hunk_mark) { - output_color(opt, "--", 2, opt->color_sep); + output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]); opt->output(opt, "\n", 1); } } else if (lno > opt->last_shown + 1) { - output_color(opt, "--", 2, opt->color_sep); + output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]); opt->output(opt, "\n", 1); } } if (opt->heading && opt->last_shown == 0) { - output_color(opt, name, strlen(name), opt->color_filename); + output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]); opt->output(opt, "\n", 1); } opt->last_shown = lno; if (!opt->heading && opt->pathname) { - output_color(opt, name, strlen(name), opt->color_filename); + output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]); output_sep(opt, sign); } if (opt->linenum) { char buf[32]; xsnprintf(buf, sizeof(buf), "%d", lno); - output_color(opt, buf, strlen(buf), opt->color_lineno); + output_color(opt, buf, strlen(buf), opt->colors[GREP_COLOR_LINENO]); output_sep(opt, sign); } if (opt->color) { @@ -1403,15 +1395,15 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, int eflags = 0; if (sign == ':') - match_color = opt->color_match_selected; + match_color = opt->colors[GREP_COLOR_MATCH_SELECTED]; else - match_color = opt->color_match_context; + match_color = opt->colors[GREP_COLOR_MATCH_CONTEXT]; if (sign == ':') - line_color = opt->color_selected; + line_color = opt->colors[GREP_COLOR_SELECTED]; else if (sign == '-') - line_color = opt->color_context; + line_color = opt->colors[GREP_COLOR_CONTEXT]; else if (sign == '=') - line_color = opt->color_function; + line_color = opt->colors[GREP_COLOR_FUNCTION]; *eol = '\0'; while (next_match(opt, bol, eol, ctx, &match, eflags)) { if (match.rm_so == match.rm_eo) @@ -1818,7 +1810,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle if (binary_match_only) { opt->output(opt, "Binary file ", 12); output_color(opt, gs->name, strlen(gs->name), - opt->color_filename); + opt->colors[GREP_COLOR_FILENAME]); opt->output(opt, " matches\n", 9); return 1; } @@ -1892,7 +1884,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle char buf[32]; if (opt->pathname) { output_color(opt, gs->name, strlen(gs->name), - opt->color_filename); + opt->colors[GREP_COLOR_FILENAME]); output_sep(opt, ':'); } xsnprintf(buf, sizeof(buf), "%u\n", count); diff --git a/grep.h b/grep.h index 399381c908..ed25be271b 100644 --- a/grep.h +++ b/grep.h @@ -62,6 +62,18 @@ enum grep_header_field { GREP_HEADER_FIELD_MAX }; +enum grep_color { + GREP_COLOR_CONTEXT, + GREP_COLOR_FILENAME, + GREP_COLOR_FUNCTION, + GREP_COLOR_LINENO, + GREP_COLOR_MATCH_CONTEXT, + GREP_COLOR_MATCH_SELECTED, + GREP_COLOR_SELECTED, + GREP_COLOR_SEP, + NR_GREP_COLORS +}; + struct grep_pat { struct grep_pat *next; const char *origin; @@ -155,14 +167,7 @@ struct grep_opt { int funcbody; int extended_regexp_option; int pattern_type_option; - char color_context[COLOR_MAXLEN]; - char color_filename[COLOR_MAXLEN]; - char color_function[COLOR_MAXLEN]; - char color_lineno[COLOR_MAXLEN]; - char color_match_context[COLOR_MAXLEN]; - char color_match_selected[COLOR_MAXLEN]; - char color_selected[COLOR_MAXLEN]; - char color_sep[COLOR_MAXLEN]; + char colors[NR_GREP_COLORS][COLOR_MAXLEN]; unsigned pre_context; unsigned post_context; unsigned last_shown; From a46baac61ebc6a8b187f76bcd49b625e0d4f408e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:23 +0200 Subject: [PATCH 03/12] fsck: factor out msg_id_info[] lazy initialization code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This array will be used by some other function than parse_msg_id() in the following commit. Factor out this prep code so it could be called from that one. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- fsck.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/fsck.c b/fsck.c index 640422a6c6..c004a37372 100644 --- a/fsck.c +++ b/fsck.c @@ -84,26 +84,34 @@ static struct { }; #undef MSG_ID -static int parse_msg_id(const char *text) +static void prepare_msg_ids(void) { int i; - if (!msg_id_info[0].downcased) { - /* convert id_string to lower case, without underscores. */ - for (i = 0; i < FSCK_MSG_MAX; i++) { - const char *p = msg_id_info[i].id_string; - int len = strlen(p); - char *q = xmalloc(len); - - msg_id_info[i].downcased = q; - while (*p) - if (*p == '_') - p++; - else - *(q)++ = tolower(*(p)++); - *q = '\0'; - } + if (msg_id_info[0].downcased) + return; + + /* convert id_string to lower case, without underscores. */ + for (i = 0; i < FSCK_MSG_MAX; i++) { + const char *p = msg_id_info[i].id_string; + int len = strlen(p); + char *q = xmalloc(len); + + msg_id_info[i].downcased = q; + while (*p) + if (*p == '_') + p++; + else + *(q)++ = tolower(*(p)++); + *q = '\0'; } +} + +static int parse_msg_id(const char *text) +{ + int i; + + prepare_msg_ids(); for (i = 0; i < FSCK_MSG_MAX; i++) if (!strcmp(text, msg_id_info[i].downcased)) From 3ac68a93fd2b984e2a7e570217d2646a208ffcc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:24 +0200 Subject: [PATCH 04/12] help: add --config to list all available config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes it helps to list all available config vars so the user can search for something they want. The config man page can also be used but it's harder to search if you want to focus on the variable name, for example. This is not the best way to collect the available config since it's not precise. Ideally we should have a centralized list of config in C code (pretty much like 'struct option'), but that's a lot more work. This will do for now. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- Documentation/git-help.txt | 5 ++++ advice.c | 9 ++++++ builtin/branch.c | 3 ++ builtin/clean.c | 3 ++ builtin/commit.c | 3 ++ builtin/help.c | 9 ++++++ diff.c | 3 ++ fsck.c | 12 ++++++++ generate-cmdlist.sh | 19 +++++++++++++ grep.c | 3 ++ help.c | 56 ++++++++++++++++++++++++++++++++++++++ help.h | 45 +++++++++++++++++++++++++++++- log-tree.c | 3 ++ 13 files changed, 172 insertions(+), 1 deletion(-) diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt index a40fc38d8b..83d25d825a 100644 --- a/Documentation/git-help.txt +++ b/Documentation/git-help.txt @@ -45,6 +45,11 @@ OPTIONS When used with `--verbose` print description for all recognized commands. +-c:: +--config:: + List all available configuration variables. This is a short + summary of the list in linkgit:git-config[1]. + -g:: --guides:: Prints a list of useful guides on the standard output. This diff --git a/advice.c b/advice.c index 370a56d054..cf6c673ed7 100644 --- a/advice.c +++ b/advice.c @@ -1,6 +1,7 @@ #include "cache.h" #include "config.h" #include "color.h" +#include "help.h" int advice_push_update_rejected = 1; int advice_push_non_ff_current = 1; @@ -131,6 +132,14 @@ int git_default_advice_config(const char *var, const char *value) return 0; } +void list_config_advices(struct string_list *list, const char *prefix) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(advice_config); i++) + list_config_item(list, prefix, advice_config[i].name); +} + int error_resolve_conflict(const char *me) { if (!strcmp(me, "cherry-pick")) diff --git a/builtin/branch.c b/builtin/branch.c index 136d57caa4..426c20b61d 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -22,6 +22,7 @@ #include "wt-status.h" #include "ref-filter.h" #include "worktree.h" +#include "help.h" static const char * const builtin_branch_usage[] = { N_("git branch [] [-r | -a] [--merged | --no-merged]"), @@ -67,6 +68,8 @@ static const char *color_branch_slots[] = { static struct string_list output = STRING_LIST_INIT_DUP; static unsigned int colopts; +define_list_config_array(color_branch_slots); + static int git_branch_config(const char *var, const char *value, void *cb) { const char *slot_name; diff --git a/builtin/clean.c b/builtin/clean.c index 0ccd95e693..ab402c204c 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -16,6 +16,7 @@ #include "column.h" #include "color.h" #include "pathspec.h" +#include "help.h" static int force = -1; /* unset */ static int interactive; @@ -91,6 +92,8 @@ struct menu_stuff { void *stuff; }; +define_list_config_array(color_interactive_slots); + static int git_clean_config(const char *var, const char *value, void *cb) { const char *slot_name; diff --git a/builtin/commit.c b/builtin/commit.c index f96755aa4b..8bb3911614 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -32,6 +32,7 @@ #include "column.h" #include "sequencer.h" #include "mailmap.h" +#include "help.h" static const char * const builtin_commit_usage[] = { N_("git commit [] [--] ..."), @@ -1185,6 +1186,8 @@ static int dry_run_commit(int argc, const char **argv, const char *prefix, return commitable ? 0 : 1; } +define_list_config_array_extra(color_status_slots, {"added"}); + static int parse_status_slot(const char *slot) { if (!strcasecmp(slot, "added")) diff --git a/builtin/help.c b/builtin/help.c index 58e0a5507f..ccb206e1d4 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -37,6 +37,7 @@ static const char *html_path; static int show_all = 0; static int show_guides = 0; +static int show_config; static int verbose; static unsigned int colopts; static enum help_format help_format = HELP_FORMAT_NONE; @@ -45,6 +46,7 @@ static struct option builtin_help_options[] = { OPT_BOOL('a', "all", &show_all, N_("print all available commands")), OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")), OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")), + OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")), OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN), OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"), HELP_FORMAT_WEB), @@ -444,6 +446,13 @@ int cmd_help(int argc, const char **argv, const char *prefix) list_commands(colopts, &main_cmds, &other_cmds); } + if (show_config) { + setup_pager(); + list_config_help(); + printf("\n%s\n", _("'git help config' for more information")); + return 0; + } + if (show_guides) list_common_guides_help(); diff --git a/diff.c b/diff.c index 56d7bfded6..513410d46b 100644 --- a/diff.c +++ b/diff.c @@ -22,6 +22,7 @@ #include "argv-array.h" #include "graph.h" #include "packfile.h" +#include "help.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -93,6 +94,8 @@ static NORETURN void die_want_option(const char *option_name) die(_("option '%s' requires a value"), option_name); } +define_list_config_array_extra(color_diff_slots, {"plain"}); + static int parse_diff_color_slot(const char *var) { if (!strcasecmp(var, "plain")) diff --git a/fsck.c b/fsck.c index c004a37372..878a3c617c 100644 --- a/fsck.c +++ b/fsck.c @@ -10,6 +10,7 @@ #include "utf8.h" #include "sha1-array.h" #include "decorate.h" +#include "help.h" #define FSCK_FATAL -1 #define FSCK_INFO -2 @@ -120,6 +121,17 @@ static int parse_msg_id(const char *text) return -1; } +void list_config_fsck_msg_ids(struct string_list *list, const char *prefix) +{ + int i; + + prepare_msg_ids(); + + /* TODO: we can do better by producing camelCase names */ + for (i = 0; i < FSCK_MSG_MAX; i++) + list_config_item(list, prefix, msg_id_info[i].downcased); +} + static int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options) { diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh index 8d6d8b45ce..c4124acbe7 100755 --- a/generate-cmdlist.sh +++ b/generate-cmdlist.sh @@ -76,6 +76,23 @@ print_command_list () { echo "};" } +print_config_list () { + cat <", list_config_color_branch_slots }, + { "color.decorate", "", list_config_color_decorate_slots }, + { "color.diff", "", list_config_color_diff_slots }, + { "color.grep", "", list_config_color_grep_slots }, + { "color.interactive", "", list_config_color_interactive_slots }, + { "color.status", "", list_config_color_status_slots }, + { "fsck", "", list_config_fsck_msg_ids }, + { "receive.fsck", "", list_config_fsck_msg_ids }, + { NULL, NULL, NULL } + }; + const char **p; + struct slot_expansion *e; + struct string_list keys = STRING_LIST_INIT_DUP; + int i; + + for (p = config_name_list; *p; p++) { + const char *var = *p; + struct strbuf sb = STRBUF_INIT; + + for (e = slot_expansions; e->prefix; e++) { + + strbuf_reset(&sb); + strbuf_addf(&sb, "%s.%s", e->prefix, e->placeholder); + if (!strcasecmp(var, sb.buf)) { + e->fn(&keys, e->prefix); + e->found++; + break; + } + } + strbuf_release(&sb); + if (!e->prefix) + string_list_append(&keys, var); + } + + for (e = slot_expansions; e->prefix; e++) + if (!e->found) + BUG("slot_expansion %s.%s is not used", + e->prefix, e->placeholder); + + string_list_sort(&keys); + for (i = 0; i < keys.nr; i++) + puts(keys.items[i].string); + string_list_clear(&keys, 0); +} + void list_all_cmds_help(void) { print_cmd_by_category(main_categories); diff --git a/help.h b/help.h index 3b38292a1b..b46fe6ee73 100644 --- a/help.h +++ b/help.h @@ -1,7 +1,8 @@ #ifndef HELP_H #define HELP_H -struct string_list; +#include "string-list.h" +#include "strbuf.h" struct cmdnames { int alloc; @@ -21,6 +22,7 @@ static inline void mput_char(char c, unsigned int num) extern void list_common_cmds_help(void); extern void list_all_cmds_help(void); extern void list_common_guides_help(void); +extern void list_config_help(void); extern void list_all_main_cmds(struct string_list *list); extern void list_all_other_cmds(struct string_list *list); @@ -42,4 +44,45 @@ extern void list_commands(unsigned int colopts, struct cmdnames *main_cmds, stru * ref to the command, to give suggested "correct" refs. */ extern void help_unknown_ref(const char *ref, const char *cmd, const char *error); + +static inline void list_config_item(struct string_list *list, + const char *prefix, + const char *str) +{ + string_list_append_nodup(list, xstrfmt("%s.%s", prefix, str)); +} + +#define define_list_config_array(array) \ +void list_config_##array(struct string_list *list, const char *prefix) \ +{ \ + int i; \ + for (i = 0; i < ARRAY_SIZE(array); i++) \ + if (array[i]) \ + list_config_item(list, prefix, array[i]); \ +} \ +struct string_list + +#define define_list_config_array_extra(array, values) \ +void list_config_##array(struct string_list *list, const char *prefix) \ +{ \ + int i; \ + static const char *extra[] = values; \ + for (i = 0; i < ARRAY_SIZE(extra); i++) \ + list_config_item(list, prefix, extra[i]); \ + for (i = 0; i < ARRAY_SIZE(array); i++) \ + if (array[i]) \ + list_config_item(list, prefix, array[i]); \ +} \ +struct string_list + +/* These are actually scattered over many C files */ +void list_config_advices(struct string_list *list, const char *prefix); +void list_config_color_branch_slots(struct string_list *list, const char *prefix); +void list_config_color_decorate_slots(struct string_list *list, const char *prefix); +void list_config_color_diff_slots(struct string_list *list, const char *prefix); +void list_config_color_grep_slots(struct string_list *list, const char *prefix); +void list_config_color_interactive_slots(struct string_list *list, const char *prefix); +void list_config_color_status_slots(struct string_list *list, const char *prefix); +void list_config_fsck_msg_ids(struct string_list *list, const char *prefix); + #endif /* HELP_H */ diff --git a/log-tree.c b/log-tree.c index d85e179077..30706fe926 100644 --- a/log-tree.c +++ b/log-tree.c @@ -12,6 +12,7 @@ #include "gpg-interface.h" #include "sequencer.h" #include "line-log.h" +#include "help.h" static struct decoration name_decoration = { "object names" }; static int decoration_loaded; @@ -42,6 +43,8 @@ static const char *decorate_get_color(int decorate_use_color, enum decoration_ty return ""; } +define_list_config_array(color_decorate_slots); + int parse_decorate_color_config(const char *var, const char *slot_name, const char *value) { int slot = LOOKUP_CONFIG(color_decorate_slots, slot_name); From a4a9cc19a29df22f367fde50eec046ba6c5533f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:25 +0200 Subject: [PATCH 05/12] fsck: produce camelCase config key names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- fsck.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/fsck.c b/fsck.c index 878a3c617c..c2b8974c0e 100644 --- a/fsck.c +++ b/fsck.c @@ -74,14 +74,15 @@ enum fsck_msg_id { #undef MSG_ID #define STR(x) #x -#define MSG_ID(id, msg_type) { STR(id), NULL, FSCK_##msg_type }, +#define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type }, static struct { const char *id_string; const char *downcased; + const char *camelcased; int msg_type; } msg_id_info[FSCK_MSG_MAX + 1] = { FOREACH_MSG_ID(MSG_ID) - { NULL, NULL, -1 } + { NULL, NULL, NULL, -1 } }; #undef MSG_ID @@ -105,6 +106,20 @@ static void prepare_msg_ids(void) else *(q)++ = tolower(*(p)++); *q = '\0'; + + p = msg_id_info[i].id_string; + q = xmalloc(len); + msg_id_info[i].camelcased = q; + while (*p) { + if (*p == '_') { + p++; + if (*p) + *q++ = *p++; + } else { + *q++ = tolower(*p++); + } + } + *q = '\0'; } } @@ -127,9 +142,8 @@ void list_config_fsck_msg_ids(struct string_list *list, const char *prefix) prepare_msg_ids(); - /* TODO: we can do better by producing camelCase names */ for (i = 0; i < FSCK_MSG_MAX; i++) - list_config_item(list, prefix, msg_id_info[i].downcased); + list_config_item(list, prefix, msg_id_info[i].camelcased); } static int fsck_msg_type(enum fsck_msg_id msg_id, From fb6fbffbdae719805fbf7a39ca268dad17696f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:26 +0200 Subject: [PATCH 06/12] advice: keep config name in camelCase in advice_config[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For parsing, we don't really need this because the main config parser will lowercase everything so we can do exact matching. But this array now is also used for printing in `git help --config`. Keep camelCase so we have a nice printout. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- advice.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/advice.c b/advice.c index cf6c673ed7..2aca11f45e 100644 --- a/advice.c +++ b/advice.c @@ -54,28 +54,28 @@ static struct { const char *name; int *preference; } advice_config[] = { - { "pushupdaterejected", &advice_push_update_rejected }, - { "pushnonffcurrent", &advice_push_non_ff_current }, - { "pushnonffmatching", &advice_push_non_ff_matching }, - { "pushalreadyexists", &advice_push_already_exists }, - { "pushfetchfirst", &advice_push_fetch_first }, - { "pushneedsforce", &advice_push_needs_force }, - { "statushints", &advice_status_hints }, - { "statusuoption", &advice_status_u_option }, - { "commitbeforemerge", &advice_commit_before_merge }, - { "resolveconflict", &advice_resolve_conflict }, - { "implicitidentity", &advice_implicit_identity }, - { "detachedhead", &advice_detached_head }, - { "setupstreamfailure", &advice_set_upstream_failure }, - { "objectnamewarning", &advice_object_name_warning }, - { "rmhints", &advice_rm_hints }, - { "addembeddedrepo", &advice_add_embedded_repo }, - { "ignoredhook", &advice_ignored_hook }, - { "waitingforeditor", &advice_waiting_for_editor }, - { "graftfiledeprecated", &advice_graft_file_deprecated }, + { "pushUpdateRejected", &advice_push_update_rejected }, + { "pushNonFFCurrent", &advice_push_non_ff_current }, + { "pushNonFFMatching", &advice_push_non_ff_matching }, + { "pushAlreadyExists", &advice_push_already_exists }, + { "pushFetchFirst", &advice_push_fetch_first }, + { "pushNeedsForce", &advice_push_needs_force }, + { "statusHints", &advice_status_hints }, + { "statusUoption", &advice_status_u_option }, + { "commitBeforeMerge", &advice_commit_before_merge }, + { "resolveConflict", &advice_resolve_conflict }, + { "implicitIdentity", &advice_implicit_identity }, + { "detachedHead", &advice_detached_head }, + { "setupStreamFailure", &advice_set_upstream_failure }, + { "objectNameWarning", &advice_object_name_warning }, + { "rmHints", &advice_rm_hints }, + { "addEmbeddedRepo", &advice_add_embedded_repo }, + { "ignoredHook", &advice_ignored_hook }, + { "waitingForEditor", &advice_waiting_for_editor }, + { "graftFileDeprecated", &advice_graft_file_deprecated }, /* make this an alias for backward compatibility */ - { "pushnonfastforward", &advice_push_update_rejected } + { "pushNonFastForward", &advice_push_update_rejected } }; void advise(const char *advice, ...) @@ -123,7 +123,7 @@ int git_default_advice_config(const char *var, const char *value) return 0; for (i = 0; i < ARRAY_SIZE(advice_config); i++) { - if (strcmp(k, advice_config[i].name)) + if (strcasecmp(k, advice_config[i].name)) continue; *advice_config[i].preference = git_config_bool(var, value); return 0; From 431bb23a271ef45e22ad4a2def2e0ba0a1954e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:27 +0200 Subject: [PATCH 07/12] am: move advice.amWorkDir parsing back to advice.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only benefit from this move (apart from cleaner code) is that advice.amWorkDir should now show up in `git help --config`. There should be no regression since advice config is always read by the git_default_config(). While at there, use advise() like other code. We now get "hint: " prefix and the output is stderr instead of stdout (which is also the reason for the test update because stderr is checked in a following test and the extra advice can fail it). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- advice.c | 2 ++ advice.h | 1 + builtin/am.c | 6 +----- t/t4254-am-corrupt.sh | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/advice.c b/advice.c index 2aca11f45e..52aa85bdfd 100644 --- a/advice.c +++ b/advice.c @@ -17,6 +17,7 @@ int advice_implicit_identity = 1; int advice_detached_head = 1; int advice_set_upstream_failure = 1; int advice_object_name_warning = 1; +int advice_amworkdir = 1; int advice_rm_hints = 1; int advice_add_embedded_repo = 1; int advice_ignored_hook = 1; @@ -68,6 +69,7 @@ static struct { { "detachedHead", &advice_detached_head }, { "setupStreamFailure", &advice_set_upstream_failure }, { "objectNameWarning", &advice_object_name_warning }, + { "amWorkDir", &advice_amworkdir }, { "rmHints", &advice_rm_hints }, { "addEmbeddedRepo", &advice_add_embedded_repo }, { "ignoredHook", &advice_ignored_hook }, diff --git a/advice.h b/advice.h index 9f5064e82a..7e9377864f 100644 --- a/advice.h +++ b/advice.h @@ -17,6 +17,7 @@ extern int advice_implicit_identity; extern int advice_detached_head; extern int advice_set_upstream_failure; extern int advice_object_name_warning; +extern int advice_amworkdir; extern int advice_rm_hints; extern int advice_add_embedded_repo; extern int advice_ignored_hook; diff --git a/builtin/am.c b/builtin/am.c index d834f9e62b..93130aaa35 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1827,15 +1827,11 @@ static void am_run(struct am_state *state, int resume) } if (apply_status) { - int advice_amworkdir = 1; - printf_ln(_("Patch failed at %s %.*s"), msgnum(state), linelen(state->msg), state->msg); - git_config_get_bool("advice.amworkdir", &advice_amworkdir); - if (advice_amworkdir) - printf_ln(_("Use 'git am --show-current-patch' to see the failed patch")); + advise(_("Use 'git am --show-current-patch' to see the failed patch")); die_user_resolve(state); } diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh index 168739c721..fd3bdbfe2c 100755 --- a/t/t4254-am-corrupt.sh +++ b/t/t4254-am-corrupt.sh @@ -25,7 +25,7 @@ test_expect_success setup ' # fatal: unable to write file '(null)' mode 100644: Bad address # Also, it had the unwanted side-effect of deleting f. test_expect_success 'try to apply corrupted patch' ' - test_must_fail git am bad-patch.diff 2>actual + test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual ' test_expect_success 'compare diagnostic; ensure file is still here' ' From e17ca926371dc96967f556f41c18410eea8c7d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:28 +0200 Subject: [PATCH 08/12] completion: drop the hard coded list of config vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new help option --config-for-completion is a machine friendlier version of --config where all the placeholders and wildcards are dropped, leaving only the good, completable prefixes for git-completion.bash to consume. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/help.c | 9 +- contrib/completion/git-completion.bash | 335 +------------------------ help.c | 34 ++- help.h | 2 +- 4 files changed, 49 insertions(+), 331 deletions(-) diff --git a/builtin/help.c b/builtin/help.c index ccb206e1d4..8d4f6dd301 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -47,6 +47,7 @@ static struct option builtin_help_options[] = { OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")), OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")), OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")), + OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN), OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN), OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"), HELP_FORMAT_WEB), @@ -447,8 +448,14 @@ int cmd_help(int argc, const char **argv, const char *prefix) } if (show_config) { + int for_human = show_config == 1; + + if (!for_human) { + list_config_help(for_human); + return 0; + } setup_pager(); - list_config_help(); + list_config_help(for_human); printf("\n%s\n", _("'git help config' for more information")); return 0; } diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 99dfedbd0b..1278f3b133 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1986,6 +1986,13 @@ __git_config_get_set_variables () __git config $config_file --name-only --list } +__git_config_vars= +__git_compute_config_vars () +{ + test -n "$__git_config_vars" || + __git_config_vars="$(git help --config-for-completion | sort | uniq)" +} + _git_config () { case "$prev" in @@ -2144,332 +2151,8 @@ _git_config () return ;; esac - __gitcomp " - add.ignoreErrors - advice.amWorkDir - advice.commitBeforeMerge - advice.detachedHead - advice.implicitIdentity - advice.pushAlreadyExists - advice.pushFetchFirst - advice.pushNeedsForce - advice.pushNonFFCurrent - advice.pushNonFFMatching - advice.pushUpdateRejected - advice.resolveConflict - advice.rmHints - advice.statusHints - advice.statusUoption - advice.ignoredHook - alias. - am.keepcr - am.threeWay - apply.ignorewhitespace - apply.whitespace - branch.autosetupmerge - branch.autosetuprebase - browser. - clean.requireForce - color.branch - color.branch.current - color.branch.local - color.branch.plain - color.branch.remote - color.decorate.HEAD - color.decorate.branch - color.decorate.remoteBranch - color.decorate.stash - color.decorate.tag - color.diff - color.diff.commit - color.diff.frag - color.diff.func - color.diff.meta - color.diff.new - color.diff.old - color.diff.plain - color.diff.whitespace - color.grep - color.grep.context - color.grep.filename - color.grep.function - color.grep.linenumber - color.grep.match - color.grep.selected - color.grep.separator - color.interactive - color.interactive.error - color.interactive.header - color.interactive.help - color.interactive.prompt - color.pager - color.showbranch - color.status - color.status.added - color.status.changed - color.status.header - color.status.localBranch - color.status.nobranch - color.status.remoteBranch - color.status.unmerged - color.status.untracked - color.status.updated - color.ui - commit.cleanup - commit.gpgSign - commit.status - commit.template - commit.verbose - core.abbrev - core.askpass - core.attributesfile - core.autocrlf - core.bare - core.bigFileThreshold - core.checkStat - core.commentChar - core.commitGraph - core.compression - core.createObject - core.deltaBaseCacheLimit - core.editor - core.eol - core.excludesfile - core.fileMode - core.fsyncobjectfiles - core.gitProxy - core.hideDotFiles - core.hooksPath - core.ignoreStat - core.ignorecase - core.logAllRefUpdates - core.loosecompression - core.notesRef - core.packedGitLimit - core.packedGitWindowSize - core.packedRefsTimeout - core.pager - core.precomposeUnicode - core.preferSymlinkRefs - core.preloadindex - core.protectHFS - core.protectNTFS - core.quotepath - core.repositoryFormatVersion - core.safecrlf - core.sharedRepository - core.sparseCheckout - core.splitIndex - core.sshCommand - core.symlinks - core.trustctime - core.untrackedCache - core.warnAmbiguousRefs - core.whitespace - core.worktree - credential.helper - credential.useHttpPath - credential.username - credentialCache.ignoreSIGHUP - diff.autorefreshindex - diff.external - diff.ignoreSubmodules - diff.mnemonicprefix - diff.noprefix - diff.renameLimit - diff.renames - diff.statGraphWidth - diff.submodule - diff.suppressBlankEmpty - diff.tool - diff.wordRegex - diff.algorithm - difftool. - difftool.prompt - fetch.recurseSubmodules - fetch.unpackLimit - format.attach - format.cc - format.coverLetter - format.from - format.headers - format.numbered - format.pretty - format.signature - format.signoff - format.subjectprefix - format.suffix - format.thread - format.to - gc. - gc.aggressiveDepth - gc.aggressiveWindow - gc.auto - gc.autoDetach - gc.autopacklimit - gc.logExpiry - gc.packrefs - gc.pruneexpire - gc.reflogexpire - gc.reflogexpireunreachable - gc.rerereresolved - gc.rerereunresolved - gc.worktreePruneExpire - gitcvs.allbinary - gitcvs.commitmsgannotation - gitcvs.dbTableNamePrefix - gitcvs.dbdriver - gitcvs.dbname - gitcvs.dbpass - gitcvs.dbuser - gitcvs.enabled - gitcvs.logfile - gitcvs.usecrlfattr - guitool. - gui.blamehistoryctx - gui.commitmsgwidth - gui.copyblamethreshold - gui.diffcontext - gui.encoding - gui.fastcopyblame - gui.matchtrackingbranch - gui.newbranchtemplate - gui.pruneduringfetch - gui.spellingdictionary - gui.trustmtime - help.autocorrect - help.browser - help.format - http.lowSpeedLimit - http.lowSpeedTime - http.maxRequests - http.minSessions - http.noEPSV - http.postBuffer - http.proxy - http.sslCipherList - http.sslVersion - http.sslCAInfo - http.sslCAPath - http.sslCert - http.sslCertPasswordProtected - http.sslKey - http.sslVerify - http.useragent - i18n.commitEncoding - i18n.logOutputEncoding - imap.authMethod - imap.folder - imap.host - imap.pass - imap.port - imap.preformattedHTML - imap.sslverify - imap.tunnel - imap.user - init.templatedir - instaweb.browser - instaweb.httpd - instaweb.local - instaweb.modulepath - instaweb.port - interactive.singlekey - log.date - log.decorate - log.showroot - mailmap.file - man. - man.viewer - merge. - merge.conflictstyle - merge.log - merge.renameLimit - merge.renormalize - merge.stat - merge.tool - merge.verbosity - mergetool. - mergetool.keepBackup - mergetool.keepTemporaries - mergetool.prompt - notes.displayRef - notes.rewrite. - notes.rewrite.amend - notes.rewrite.rebase - notes.rewriteMode - notes.rewriteRef - pack.compression - pack.deltaCacheLimit - pack.deltaCacheSize - pack.depth - pack.indexVersion - pack.packSizeLimit - pack.threads - pack.window - pack.windowMemory - pager. - pretty. - pull.octopus - pull.twohead - push.default - push.followTags - rebase.autosquash - rebase.stat - receive.autogc - receive.denyCurrentBranch - receive.denyDeleteCurrent - receive.denyDeletes - receive.denyNonFastForwards - receive.fsckObjects - receive.unpackLimit - receive.updateserverinfo - remote.pushdefault - remotes. - repack.usedeltabaseoffset - rerere.autoupdate - rerere.enabled - sendemail. - sendemail.aliasesfile - sendemail.aliasfiletype - sendemail.bcc - sendemail.cc - sendemail.cccmd - sendemail.chainreplyto - sendemail.confirm - sendemail.envelopesender - sendemail.from - sendemail.identity - sendemail.multiedit - sendemail.signedoffbycc - sendemail.smtpdomain - sendemail.smtpencryption - sendemail.smtppass - sendemail.smtpserver - sendemail.smtpserveroption - sendemail.smtpserverport - sendemail.smtpuser - sendemail.suppresscc - sendemail.suppressfrom - sendemail.thread - sendemail.to - sendemail.tocmd - sendemail.validate - sendemail.smtpbatchsize - sendemail.smtprelogindelay - showbranch.default - status.relativePaths - status.showUntrackedFiles - status.submodulesummary - submodule. - tar.umask - transfer.unpackLimit - url. - user.email - user.name - user.signingkey - web.browser - branch. remote. - " + __git_compute_config_vars + __gitcomp "$__git_config_vars" } _git_remote () diff --git a/help.c b/help.c index f078dfebad..3ebf0568db 100644 --- a/help.c +++ b/help.c @@ -416,7 +416,7 @@ struct slot_expansion { int found; }; -void list_config_help(void) +void list_config_help(int for_human) { struct slot_expansion slot_expansions[] = { { "advice", "*", list_config_advices }, @@ -460,8 +460,36 @@ void list_config_help(void) e->prefix, e->placeholder); string_list_sort(&keys); - for (i = 0; i < keys.nr; i++) - puts(keys.items[i].string); + for (i = 0; i < keys.nr; i++) { + const char *var = keys.items[i].string; + const char *wildcard, *tag, *cut; + + if (for_human) { + puts(var); + continue; + } + + wildcard = strchr(var, '*'); + tag = strchr(var, '<'); + + if (!wildcard && !tag) { + puts(var); + continue; + } + + if (wildcard && !tag) + cut = wildcard; + else if (!wildcard && tag) + cut = tag; + else + cut = wildcard < tag ? wildcard : tag; + + /* + * We may produce duplicates, but that's up to + * git-completion.bash to handle + */ + printf("%.*s\n", (int)(cut - var), var); + } string_list_clear(&keys, 0); } diff --git a/help.h b/help.h index b46fe6ee73..f8b15323a6 100644 --- a/help.h +++ b/help.h @@ -22,7 +22,7 @@ static inline void mput_char(char c, unsigned int num) extern void list_common_cmds_help(void); extern void list_all_cmds_help(void); extern void list_common_guides_help(void); -extern void list_config_help(void); +extern void list_config_help(int for_human); extern void list_all_main_cmds(struct string_list *list); extern void list_all_other_cmds(struct string_list *list); From f45db831c17d0a32d2e1cd0f5ac40708020bcf8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:29 +0200 Subject: [PATCH 09/12] completion: keep other config var completion in camelCase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last patch makes "git config " shows camelCase names because that's what's in the source: config.txt. There are still a couple manual var completion in this code. Let's make them follow the naming convention as well. In theory we could automate this part too because we have the information. But let's stick to one step at a time and leave this for later. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1278f3b133..e74d50a4b9 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2093,20 +2093,20 @@ _git_config () ;; branch.*.*) local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "remote pushremote merge mergeoptions rebase" "$pfx" "$cur_" + __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" return ;; branch.*) local pfx="${cur%.*}." cur_="${cur#*.}" __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")" - __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_" + __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" return ;; guitool.*.*) local pfx="${cur%.*}." cur_="${cur##*.}" __gitcomp " - argprompt cmd confirm needsfile noconsole norescan - prompt revprompt revunmerged title + argPrompt cmd confirm needsFile noConsole noRescan + prompt revPrompt revUnmerged title " "$pfx" "$cur_" return ;; @@ -2135,14 +2135,14 @@ _git_config () local pfx="${cur%.*}." cur_="${cur##*.}" __gitcomp " url proxy fetch push mirror skipDefaultUpdate - receivepack uploadpack tagopt pushurl + receivepack uploadpack tagOpt pushurl " "$pfx" "$cur_" return ;; remote.*) local pfx="${cur%.*}." cur_="${cur#*.}" __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "." - __gitcomp_nl_append "pushdefault" "$pfx" "$cur_" + __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" return ;; url.*.*) From bea2125928f4d6ddad34c6e651d4a9ec7ee5dd4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:30 +0200 Subject: [PATCH 10/12] completion: support case-insensitive config vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Config variables are case-insensitive but this case/esac construct is case-sensitive by default. For bash v4, it'll be easy. For platforms that are stuck with older versions, we need an external command, but that is not that critical. And where this additional overhead matters the most is Windows, but luckily Git for Windows ships with Bash v4. Helped-by: SZEDER Gábor Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e74d50a4b9..97776fb31a 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1995,7 +1995,15 @@ __git_compute_config_vars () _git_config () { - case "$prev" in + local varname + + if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then + varname="${prev,,}" + else + varname="$(echo "$prev" |tr A-Z a-z)" + fi + + case "$varname" in branch.*.remote|branch.*.pushremote) __gitcomp_nl "$(__git_remotes)" return From 09c4ba410b0fda5a82dc5a9b71bfd4967927e6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 26 May 2018 15:55:31 +0200 Subject: [PATCH 11/12] log-tree: allow to customize 'grafted' color MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 76f5df305b (log: decorate grafted commits with "grafted" - 2011-08-18) lets us decorate grafted commits but I forgot about the color.decorate.* config. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- Documentation/config.txt | 3 ++- log-tree.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index a6cd53b59c..4bb1b6f9af 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1162,7 +1162,8 @@ color.diff.:: color.decorate.:: Use customized color for 'git log --decorate' output. `` is one of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local - branches, remote-tracking branches, tags, stash and HEAD, respectively. + branches, remote-tracking branches, tags, stash and HEAD, respectively + and `grafted` for grafted commits. color.grep:: When set to `always`, always highlight matches. When `false` (or diff --git a/log-tree.c b/log-tree.c index 30706fe926..888c236aa3 100644 --- a/log-tree.c +++ b/log-tree.c @@ -34,6 +34,7 @@ static const char *color_decorate_slots[] = { [DECORATION_REF_TAG] = "tag", [DECORATION_REF_STASH] = "stash", [DECORATION_REF_HEAD] = "HEAD", + [DECORATION_GRAFTED] = "grafted", }; static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix) From f22f682695d8f1bf79cde44cfe0b913905c1ef9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 27 May 2018 20:28:00 +0200 Subject: [PATCH 12/12] completion: complete general config vars in two steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are 581 config variables as of now when you do "git config " which can fill up a few screens and is not very helpful when you have to look through columns of text to find what you want. This patch instead shows you only first level when you do git config There are 78 items, which use up 8 rows in my screen. Compared to screens of text, it's pretty good. Once you have chosen you first level, e.g. color: git config color. will show you all color.* This is not a new idea. branch.* and remote.* completion already does this for second and third levels. For those variables, you'll need to three times to get full variable name. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 97776fb31a..a6f55e856a 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2158,9 +2158,14 @@ _git_config () __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" return ;; + *.*) + __git_compute_config_vars + __gitcomp "$__git_config_vars" + ;; + *) + __git_compute_config_vars + __gitcomp "$(echo "$__git_config_vars" | sed 's/\.[^ ]*/./g')" esac - __git_compute_config_vars - __gitcomp "$__git_config_vars" } _git_remote ()