提交 ebaf0a56 编写于 作者: J 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
......@@ -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
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_remote ()
......
......@@ -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
......@@ -69,46 +70,37 @@ 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);
}
define_list_config_array_extra(color_diff_slots, {"plain"});
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,
......
......@@ -14,6 +14,7 @@
#include "packfile.h"
#include "submodule-config.h"
#include "config.h"
#include "help.h"
static struct oidset gitmodules_found = OIDSET_INIT;
static struct oidset gitmodules_done = OIDSET_INIT;
......@@ -86,37 +87,60 @@ 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
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';
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';
}
}
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))
......@@ -125,6 +149,16 @@ 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();
for (i = 0; i < FSCK_MSG_MAX; i++)
list_config_item(list, prefix, msg_id_info[i].camelcased);
}
static int fsck_msg_type(enum fsck_msg_id msg_id,
struct fsck_options *options)
{
......
......@@ -76,6 +76,23 @@ print_command_list () {
echo "};"
}
print_config_list () {
cat <<EOF
static const char *config_name_list[] = {
EOF
grep '^[a-zA-Z].*\..*::$' Documentation/config.txt |
sed '/deprecated/d; s/::$//; s/, */\n/g' |
sort |
while read line
do
echo " \"$line\","
done
cat <<EOF
NULL,
};
EOF
}
echo "/* Automatically generated by generate-cmdlist.sh */
struct cmdname_help {
const char *name;
......@@ -88,3 +105,5 @@ echo
define_category_names "$1"
echo
print_command_list "$1"
echo
print_config_list
......@@ -7,12 +7,24 @@
#include "diffcore.h"
#include "commit.h"
#include "quote.h"
#include "help.h"
static int grep_source_load(struct grep_source *gs);
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 +54,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;
}
......@@ -69,6 +81,8 @@ static int parse_pattern_type_arg(const char *opt, const char *arg)
die("bad %s argument: %s", opt, arg);
}
define_list_config_array_extra(color_grep_slots, {"match"});
/*
* Read the configuration file once and store it in
* the grep_defaults template.
......@@ -76,7 +90,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 +117,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 +144,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 +161,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)
......@@ -1098,12 +1093,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);
}
......@@ -1370,28 +1365,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) {
......@@ -1401,15 +1396,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)
......@@ -1816,7 +1811,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;
}
......@@ -1890,7 +1885,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);
......
......@@ -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;
......
......@@ -409,6 +409,90 @@ void list_common_guides_help(void)
putchar('\n');
}
struct slot_expansion {
const char *prefix;
const char *placeholder;
void (*fn)(struct string_list *list, const char *prefix);
int found;
};
void list_config_help(int for_human)
{
struct slot_expansion slot_expansions[] = {
{ "advice", "*", list_config_advices },
{ "color.branch", "<slot>", list_config_color_branch_slots },
{ "color.decorate", "<slot>", list_config_color_decorate_slots },
{ "color.diff", "<slot>", list_config_color_diff_slots },
{ "color.grep", "<slot>", list_config_color_grep_slots },
{ "color.interactive", "<slot>", list_config_color_interactive_slots },
{ "color.status", "<slot>", list_config_color_status_slots },
{ "fsck", "<msg-id>", list_config_fsck_msg_ids },
{ "receive.fsck", "<msg-id>", 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++) {
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);
}
void list_all_cmds_help(void)
{
print_cmd_by_category(main_categories);
......
#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(int for_human);
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 */
......@@ -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;
......@@ -27,6 +28,15 @@ 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",
[DECORATION_GRAFTED] = "grafted",
};
static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix)
{
if (want_color(decorate_use_color))
......@@ -34,34 +44,11 @@ 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;
}
define_list_config_array(color_decorate_slots);
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)
......
......@@ -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' '
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册