Commit ebaf0a56 authored by Junio C Hamano's avatar Junio C Hamano

Merge branch 'nd/complete-config-vars'

Continuing with the idea to programatically enumerate various
pieces of data required for command line completion, teach the
codebase to report the list of configuration variables
subcommands care about to help complete them.

* nd/complete-config-vars:
  completion: complete general config vars in two steps
  log-tree: allow to customize 'grafted' color
  completion: support case-insensitive config vars
  completion: keep other config var completion in camelCase
  completion: drop the hard coded list of config vars
  am: move advice.amWorkDir parsing back to advice.c
  advice: keep config name in camelCase in advice_config[]
  fsck: produce camelCase config key names
  help: add --config to list all available config
  fsck: factor out msg_id_info[] lazy initialization code
  grep: keep all colors in an array
  Add and use generic name->id mapping code for color slot parsing
parents 11024058 f22f6826
......@@ -1162,7 +1162,8 @@ color.diff.<slot>::
color.decorate.<slot>::
Use customized color for 'git log --decorate' output. `<slot>` 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
......
......@@ -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
......
#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;
......@@ -16,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;
......@@ -53,28 +55,29 @@ 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 },
{ "amWorkDir", &advice_amworkdir },
{ "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, ...)
......@@ -122,7 +125,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;
......@@ -131,6 +134,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"))
......
......@@ -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;
......
......@@ -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);
}
......
......@@ -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 [<options>] [-r | -a] [--merged | --no-merged]"),
......@@ -55,25 +56,19 @@ 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;
}
define_list_config_array(color_branch_slots);
static int git_branch_config(const char *var, const char *value, void *cb)
{
......@@ -86,7 +81,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)
......
......@@ -16,6 +16,7 @@
#include "column.h"
#include "color.h"
#include "pathspec.h"
#include "help.h"
static int force = -1; /* unset */
static int interactive;
......@@ -42,6 +43,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,22 +92,7 @@ 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;
}
define_list_config_array(color_interactive_slots);
static int git_clean_config(const char *var, const char *value, void *cb)
{
......@@ -113,7 +108,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)
......
......@@ -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 [<options>] [--] <pathspec>..."),
......@@ -66,6 +67,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 */
......@@ -1183,27 +1196,14 @@ 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, "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)
......
......@@ -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,8 @@ 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_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),
......@@ -444,6 +447,19 @@ int cmd_help(int argc, const char **argv, const char *prefix)
list_commands(colopts, &main_cmds, &other_cmds);
}
if (show_config) {
int for_human = show_config == 1;
if (!for_human) {
list_config_help(for_human);
return 0;
}
setup_pager();
list_config_help(for_human);
printf("\n%s\n", _("'git help config' for more information"));
return 0;
}
if (show_guides)
list_common_guides_help();
......
......@@ -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;
}
......@@ -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 */
......@@ -2142,9 +2142,24 @@ __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
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
......@@ -2242,20 +2257,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
;;
......@@ -2284,14 +2299,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.*.*)
......@@ -2299,333 +2314,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
__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