{"thread":{"id":"48522","subject":"[PATCH 00/14] nd/command-list updates","startedAt":"2018-05-19T04:28:02Z","lastAt":"2018-11-20T19:34:10Z","messageCount":40,"participants":["Nguyễn Thái Ngọc Duy","SZEDER Gábor","Duy Nguyen","Junio C Hamano","Ævar Arnfjörð Bjarmason"],"isPatch":true,"patchVersion":1,"patchTotal":14},"messages":[{"id":"348031","messageId":"20180519042752.8666-1-pclouds@gmail.com","threadId":"48522","inReplyTo":null,"subject":"[PATCH 00/14] nd/command-list updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:38Z","receivedAt":"2018-05-19T04:28:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This should be the final update before nd/command-list hits 'next',\nhopefully.\n\nBesides the interdiff below, the old 12/13 is split into two: 12/14\nkeeps the complete output as close as possible to the base version and\n13/14 removes some commands from the completion list.\n\ninterdiff\n\ndiff --git a/command-list.txt b/command-list.txt\nindex 3e21ddfcfb..8462ea475f 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -39,8 +39,9 @@\n # mainporcelain commands are completable so you don't need this\n # attribute.\n #\n-# While not true commands, guides are also specified here, which can\n-# only have \"guide\" attribute and nothing else.\n+# As part of the Git man page list, the man(5/7) guides are also\n+# specified here, which can only have \"guide\" attribute and nothing\n+# else.\n #\n ### command list (do not change this line, also do not change alignment)\n # command name                          category [category] [category]\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex f7ca9a4d4f..f237eb0ff4 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -857,6 +857,8 @@ __git_complete_strategy ()\n \treturn 1\n }\n \n+# __git_commands requires 1 argument:\n+# 1: the command group, either \"all\" or \"porcelain\"\n __git_commands () {\n \tcase \"$1\" in\n \tporcelain)\n\n\nNguyễn Thái Ngọc Duy (14):\n  generate-cmds.sh: factor out synopsis extract code\n  generate-cmds.sh: export all commands to command-list.h\n  help: use command-list.h for common command list\n  Remove common-cmds.h\n  git.c: convert --list-* to --list-cmds=*\n  git --list-cmds: collect command list in a string_list\n  completion: implement and use --list-cmds=main,others\n  git: support --list-cmds=list-<category>\n  help: add \"-a --verbose\" to list all commands with synopsis\n  help: use command-list.txt for the source of guides\n  command-list.txt: documentation and guide line\n  completion: let git provide the completable command list\n  completion: reduce completable command list\n  completion: allow to customize the completable command list\n\n .gitignore                             |   2 +-\n Documentation/config.txt               |  10 +\n Documentation/git-help.txt             |   4 +-\n Documentation/gitattributes.txt        |   2 +-\n Documentation/gitmodules.txt           |   2 +-\n Documentation/gitrevisions.txt         |   2 +-\n Makefile                               |  16 +-\n builtin/help.c                         |  39 +---\n command-list.txt                       | 112 +++++++++---\n contrib/completion/git-completion.bash | 162 +++++++---------\n generate-cmdlist.sh                    | 126 ++++++++-----\n git.c                                  |  68 ++++++-\n help.c                                 | 244 ++++++++++++++++++++++---\n help.h                                 |  10 +\n t/t0012-help.sh                        |  26 ++-\n t/t9902-completion.sh                  |   5 +-\n 16 files changed, 576 insertions(+), 254 deletions(-)\n\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348032","messageId":"20180519042752.8666-2-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 01/14] generate-cmds.sh: factor out synopsis extract code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:39Z","receivedAt":"2018-05-19T04:28:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes it easier to reuse the same code in another place (very\nsoon).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n generate-cmdlist.sh | 18 +++++++++++-------\n 1 file changed, 11 insertions(+), 7 deletions(-)\n\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex eeea4b67ea..31b6d886cb 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -1,5 +1,15 @@\n #!/bin/sh\n \n+get_synopsis () {\n+\tsed -n '\n+\t\t/^NAME/,/'\"$1\"'/H\n+\t\t${\n+\t\t\tx\n+\t\t\ts/.*'\"$1\"' - \\(.*\\)/N_(\"\\1\")/\n+\t\t\tp\n+\t\t}' \"Documentation/$1.txt\"\n+}\n+\n echo \"/* Automatically generated by generate-cmdlist.sh */\n struct cmdname_help {\n \tchar name[16];\n@@ -39,12 +49,6 @@ sort |\n while read cmd tags\n do\n \ttag=$(echo \"$tags\" | sed \"$substnum; s/[^0-9]//g\")\n-\tsed -n '\n-\t\t/^NAME/,/git-'\"$cmd\"'/H\n-\t\t${\n-\t\t\tx\n-\t\t\ts/.*git-'\"$cmd\"' - \\(.*\\)/\t{\"'\"$cmd\"'\", N_(\"\\1\"), '$tag'},/\n-\t\t\tp\n-\t\t}' \"Documentation/git-$cmd.txt\"\n+\techo \"\t{\\\"$cmd\\\", $(get_synopsis git-$cmd), $tag},\"\n done\n echo \"};\"\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348033","messageId":"20180519042752.8666-3-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 02/14] generate-cmds.sh: export all commands to command-list.h","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:40Z","receivedAt":"2018-05-19T04:28:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The current generate-cmds.sh generates just enough to print \"git help\"\noutput. That is, it only extracts help text for common commands.\n\nThe script is now updated to extract help text for all commands and\nkeep command classification a new file, command-list.h. This will be\nuseful later:\n\n- \"git help -a\" could print a short summary of all commands instead of\n  just the common ones.\n\n- \"git\" could produce a list of commands of one or more category. One\n  of its use is to reduce another command classification embedded in\n  git-completion.bash.\n\nThe new file can be generated but is not used anywhere yet. The plan\nis we migrate away from common-cmds.h. Then we can kill off\ncommon-cmds.h build rules and generation code (and also delete\nduplicate content in command-list.h which we keep for now to not mess\ngenerate-cmds.sh up too much).\n\nPS. The new fixed column requirement on command-list.txt is\ntechnically not needed. But it helps simplify the code a bit at this\nstage. We could lift this restriction later if we want to.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore          |  1 +\n Makefile            | 13 ++++++---\n command-list.txt    |  4 +--\n generate-cmdlist.sh | 67 ++++++++++++++++++++++++++++++++++++++++++---\n 4 files changed, 75 insertions(+), 10 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 833ef3b0b7..d4c3914167 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -180,6 +180,7 @@\n /gitweb/static/gitweb.js\n /gitweb/static/gitweb.min.*\n /common-cmds.h\n+/command-list.h\n *.tar.gz\n *.dsc\n *.deb\ndiff --git a/Makefile b/Makefile\nindex f181687250..2a8913ea21 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -757,7 +757,7 @@ LIB_FILE = libgit.a\n XDIFF_LIB = xdiff/lib.a\n VCSSVN_LIB = vcs-svn/lib.a\n \n-GENERATED_H += common-cmds.h\n+GENERATED_H += common-cmds.h command-list.h\n \n LIB_H = $(shell $(FIND) . \\\n \t-name .git -prune -o \\\n@@ -1938,6 +1938,11 @@ $(BUILT_INS): git$X\n common-cmds.h: generate-cmdlist.sh command-list.txt\n \n common-cmds.h: $(wildcard Documentation/git-*.txt)\n+\t$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt COMMON >$@+ && mv $@+ $@\n+\n+command-list.h: generate-cmdlist.sh command-list.txt\n+\n+command-list.h: $(wildcard Documentation/git-*.txt)\n \t$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@\n \n SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\\\n@@ -2148,7 +2153,7 @@ else\n # Dependencies on header files, for platforms that do not support\n # the gcc -MMD option.\n #\n-# Dependencies on automatically generated headers such as common-cmds.h\n+# Dependencies on automatically generated headers such as common-cmds.h or command-list.h\n # should _not_ be included here, since they are necessary even when\n # building an object for the first time.\n \n@@ -2527,7 +2532,7 @@ sparse: $(SP_OBJ)\n style:\n \tgit clang-format --style file --diff --extensions c,h\n \n-check: common-cmds.h\n+check: common-cmds.h command-list.h\n \t@if sparse; \\\n \tthen \\\n \t\techo >&2 \"Use 'make sparse' instead\"; \\\n@@ -2775,7 +2780,7 @@ clean: profile-clean coverage-clean\n \t$(RM) $(TEST_PROGRAMS) $(NO_INSTALL)\n \t$(RM) -r bin-wrappers $(dep_dirs)\n \t$(RM) -r po/build/\n-\t$(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*\n+\t$(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h command-list.h $(ETAGS_TARGET) tags cscope*\n \t$(RM) -r $(GIT_TARNAME) .doc-tmp-dir\n \t$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\ndiff --git a/command-list.txt b/command-list.txt\nindex a1fad28fd8..786536aba0 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -8,8 +8,8 @@ info         examine the history and state (see also: git help revisions)\n history      grow, mark and tweak your common history\n remote       collaborate (see also: git help workflows)\n \n-### command list (do not change this line)\n-# command name                          category [deprecated] [common]\n+### command list (do not change this line, also do not change alignment)\n+# command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\n git-am                                  mainporcelain\n git-annotate                            ancillaryinterrogators\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex 31b6d886cb..870d3b626a 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -1,5 +1,27 @@\n #!/bin/sh\n \n+die () {\n+\techo \"$@\" >&2\n+\texit 1\n+}\n+\n+command_list () {\n+\tsed '1,/^### command list/d;/^#/d' \"$1\"\n+}\n+\n+get_categories () {\n+\ttr ' ' '\\n'|\n+\tgrep -v '^$' |\n+\tsort |\n+\tuniq\n+}\n+\n+category_list () {\n+\tcommand_list \"$1\" |\n+\tcut -c 40- |\n+\tget_categories\n+}\n+\n get_synopsis () {\n \tsed -n '\n \t\t/^NAME/,/'\"$1\"'/H\n@@ -10,14 +32,51 @@ get_synopsis () {\n \t\t}' \"Documentation/$1.txt\"\n }\n \n+define_categories () {\n+\techo\n+\techo \"/* Command categories */\"\n+\tbit=0\n+\tcategory_list \"$1\" |\n+\twhile read cat\n+\tdo\n+\t\techo \"#define CAT_$cat (1UL << $bit)\"\n+\t\tbit=$(($bit+1))\n+\tdone\n+\ttest \"$bit\" -gt 32 && die \"Urgh.. too many categories?\"\n+}\n+\n+print_command_list () {\n+\techo \"static struct cmdname_help command_list[] = {\"\n+\n+\tcommand_list \"$1\" |\n+\twhile read cmd rest\n+\tdo\n+\t\tprintf \"\t{ \\\"$cmd\\\", $(get_synopsis $cmd), 0\"\n+\t\tfor cat in $(echo \"$rest\" | get_categories)\n+\t\tdo\n+\t\t\tprintf \" | CAT_$cat\"\n+\t\tdone\n+\t\techo \" },\"\n+\tdone\n+\techo \"};\"\n+}\n+\n echo \"/* Automatically generated by generate-cmdlist.sh */\n struct cmdname_help {\n-\tchar name[16];\n-\tchar help[80];\n-\tunsigned char group;\n+\tconst char *name;\n+\tconst char *help;\n+\tuint32_t group;\n };\n+\"\n+if test -z \"$2\"\n+then\n+\tdefine_categories \"$1\"\n+\techo\n+\tprint_command_list \"$1\"\n+\texit 0\n+fi\n \n-static const char *common_cmd_groups[] = {\"\n+echo \"static const char *common_cmd_groups[] = {\"\n \n grps=grps$$.tmp\n match=match$$.tmp\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348034","messageId":"20180519042752.8666-7-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 06/14] git --list-cmds: collect command list in a string_list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:44Z","receivedAt":"2018-05-19T04:28:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Instead of printing the command directly one by one, keep them in a\nlist and print at the end. This allows more modification before we\nprint out (e.g. sorting, removing duplicates or even excluding some\nitems).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n git.c | 22 +++++++++++++++++-----\n 1 file changed, 17 insertions(+), 5 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex cd85355d81..376a59b97f 100644\n--- a/git.c\n+++ b/git.c\n@@ -36,7 +36,7 @@ const char git_more_info_string[] =\n \n static int use_pager = -1;\n \n-static void list_builtins(unsigned int exclude_option, char sep);\n+static void list_builtins(struct string_list *list, unsigned int exclude_option);\n \n static int match_token(const char *spec, int len, const char *token)\n {\n@@ -47,18 +47,24 @@ static int match_token(const char *spec, int len, const char *token)\n \n static int list_cmds(const char *spec)\n {\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tint i;\n+\n \twhile (*spec) {\n \t\tconst char *sep = strchrnul(spec, ',');\n \t\tint len = sep - spec;\n \n \t\tif (match_token(spec, len, \"builtins\"))\n-\t\t\tlist_builtins(0, '\\n');\n+\t\t\tlist_builtins(&list, 0);\n \t\telse\n \t\t\tdie(_(\"unsupported command listing type '%s'\"), spec);\n \t\tspec += len;\n \t\tif (*spec == ',')\n \t\t\tspec++;\n \t}\n+\tfor (i = 0; i < list.nr; i++)\n+\t\tputs(list.items[i].string);\n+\tstring_list_clear(&list, 0);\n \treturn 0;\n }\n \n@@ -249,7 +255,13 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\t(*argc)--;\n \t\t} else if (skip_prefix(cmd, \"--list-cmds=\", &cmd)) {\n \t\t\tif (!strcmp(cmd, \"parseopt\")) {\n-\t\t\t\tlist_builtins(NO_PARSEOPT, ' ');\n+\t\t\t\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\t\t\t\tint i;\n+\n+\t\t\t\tlist_builtins(&list, NO_PARSEOPT);\n+\t\t\t\tfor (i = 0; i < list.nr; i++)\n+\t\t\t\t\tprintf(\"%s \", list.items[i].string);\n+\t\t\t\tstring_list_clear(&list, 0);\n \t\t\t\texit(0);\n \t\t\t} else {\n \t\t\t\texit(list_cmds(cmd));\n@@ -533,14 +545,14 @@ int is_builtin(const char *s)\n \treturn !!get_builtin(s);\n }\n \n-static void list_builtins(unsigned int exclude_option, char sep)\n+static void list_builtins(struct string_list *out, unsigned int exclude_option)\n {\n \tint i;\n \tfor (i = 0; i < ARRAY_SIZE(commands); i++) {\n \t\tif (exclude_option &&\n \t\t    (commands[i].option & exclude_option))\n \t\t\tcontinue;\n-\t\tprintf(\"%s%c\", commands[i].cmd, sep);\n+\t\tstring_list_append(out, commands[i].cmd);\n \t}\n }\n \n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348035","messageId":"20180519042752.8666-8-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 07/14] completion: implement and use --list-cmds=main,others","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:45Z","receivedAt":"2018-05-19T04:28:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is part of the effort to break down and provide commands by\ncategory in machine-readable form. This could be helpful later on when\ncompletion script switches to use --list-cmds for selecting\ncompletable commands. It would be much easier for the user to choose\nto complete _all_ commands instead of the default selection by passing\ndifferent values to --list-cmds in git-completino.bash.\n\nWhile at there, replace \"git help -a\" in git-completion.bash with\n--list-cmds since it's better suited for this task.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n contrib/completion/git-completion.bash |  2 +-\n git.c                                  |  4 ++++\n help.c                                 | 32 ++++++++++++++++++++++++++\n help.h                                 |  4 ++++\n 4 files changed, 41 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 3556838759..62ca8641f4 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -839,7 +839,7 @@ __git_commands () {\n \tthen\n \t\tprintf \"%s\" \"${GIT_TESTING_COMMAND_COMPLETION}\"\n \telse\n-\t\tgit help -a|egrep '^  [a-zA-Z0-9]'\n+\t\tgit --list-cmds=main,others\n \tfi\n }\n \ndiff --git a/git.c b/git.c\nindex 376a59b97f..10907f7266 100644\n--- a/git.c\n+++ b/git.c\n@@ -56,6 +56,10 @@ static int list_cmds(const char *spec)\n \n \t\tif (match_token(spec, len, \"builtins\"))\n \t\t\tlist_builtins(&list, 0);\n+\t\telse if (match_token(spec, len, \"main\"))\n+\t\t\tlist_all_main_cmds(&list);\n+\t\telse if (match_token(spec, len, \"others\"))\n+\t\t\tlist_all_other_cmds(&list);\n \t\telse\n \t\t\tdie(_(\"unsupported command listing type '%s'\"), spec);\n \t\tspec += len;\ndiff --git a/help.c b/help.c\nindex 2d6a3157f8..d5ce9dfcbb 100644\n--- a/help.c\n+++ b/help.c\n@@ -297,6 +297,38 @@ void list_common_cmds_help(void)\n \tprint_cmd_by_category(common_categories);\n }\n \n+void list_all_main_cmds(struct string_list *list)\n+{\n+\tstruct cmdnames main_cmds, other_cmds;\n+\tint i;\n+\n+\tmemset(&main_cmds, 0, sizeof(main_cmds));\n+\tmemset(&other_cmds, 0, sizeof(other_cmds));\n+\tload_command_list(\"git-\", &main_cmds, &other_cmds);\n+\n+\tfor (i = 0; i < main_cmds.cnt; i++)\n+\t\tstring_list_append(list, main_cmds.names[i]->name);\n+\n+\tclean_cmdnames(&main_cmds);\n+\tclean_cmdnames(&other_cmds);\n+}\n+\n+void list_all_other_cmds(struct string_list *list)\n+{\n+\tstruct cmdnames main_cmds, other_cmds;\n+\tint i;\n+\n+\tmemset(&main_cmds, 0, sizeof(main_cmds));\n+\tmemset(&other_cmds, 0, sizeof(other_cmds));\n+\tload_command_list(\"git-\", &main_cmds, &other_cmds);\n+\n+\tfor (i = 0; i < other_cmds.cnt; i++)\n+\t\tstring_list_append(list, other_cmds.names[i]->name);\n+\n+\tclean_cmdnames(&main_cmds);\n+\tclean_cmdnames(&other_cmds);\n+}\n+\n int is_in_cmdlist(struct cmdnames *c, const char *s)\n {\n \tint i;\ndiff --git a/help.h b/help.h\nindex b21d7c94e8..97e6c0965e 100644\n--- a/help.h\n+++ b/help.h\n@@ -1,6 +1,8 @@\n #ifndef HELP_H\n #define HELP_H\n \n+struct string_list;\n+\n struct cmdnames {\n \tint alloc;\n \tint cnt;\n@@ -17,6 +19,8 @@ static inline void mput_char(char c, unsigned int num)\n }\n \n extern void list_common_cmds_help(void);\n+extern void list_all_main_cmds(struct string_list *list);\n+extern void list_all_other_cmds(struct string_list *list);\n extern const char *help_unknown_cmd(const char *cmd);\n extern void load_command_list(const char *prefix,\n \t\t\t      struct cmdnames *main_cmds,\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348036","messageId":"20180519042752.8666-10-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 09/14] help: add \"-a --verbose\" to list all commands with synopsis","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:47Z","receivedAt":"2018-05-19T04:28:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This lists all recognized commands [1] by category. The group order\nfollows closely git.txt.\n\n[1] We may actually show commands that are not built (e.g. if you set\nNO_PERL you don't have git-instaweb but it's still listed here). I\nignore the problem because on Linux a git package could be split\nanyway. The \"git-core\" package may not contain git-instaweb even if\nit's built because it may end up in a separate package. We can't know\nanyway.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-help.txt |  4 +++-\n builtin/help.c             |  7 +++++++\n help.c                     | 16 ++++++++++++++++\n help.h                     |  2 ++\n t/t0012-help.sh            |  9 +++++++++\n 5 files changed, 37 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-help.txt b/Documentation/git-help.txt\nindex 40d328a4b3..a40fc38d8b 100644\n--- a/Documentation/git-help.txt\n+++ b/Documentation/git-help.txt\n@@ -8,7 +8,7 @@ git-help - Display help information about Git\n SYNOPSIS\n --------\n [verse]\n-'git help' [-a|--all] [-g|--guide]\n+'git help' [-a|--all [--verbose]] [-g|--guide]\n \t   [-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]\n \n DESCRIPTION\n@@ -42,6 +42,8 @@ OPTIONS\n --all::\n \tPrints all the available commands on the standard output. This\n \toption overrides any given command or guide name.\n+\tWhen used with `--verbose` print description for all recognized\n+\tcommands.\n \n -g::\n --guides::\ndiff --git a/builtin/help.c b/builtin/help.c\nindex 598867cfea..0e0af8426a 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -36,6 +36,7 @@ static const char *html_path;\n \n static int show_all = 0;\n static int show_guides = 0;\n+static int verbose;\n static unsigned int colopts;\n static enum help_format help_format = HELP_FORMAT_NONE;\n static int exclude_guides;\n@@ -48,6 +49,7 @@ static struct option builtin_help_options[] = {\n \t\t\tHELP_FORMAT_WEB),\n \tOPT_SET_INT('i', \"info\", &help_format, N_(\"show info page\"),\n \t\t\tHELP_FORMAT_INFO),\n+\tOPT__VERBOSE(&verbose, N_(\"print command description\")),\n \tOPT_END(),\n };\n \n@@ -463,6 +465,11 @@ int cmd_help(int argc, const char **argv, const char *prefix)\n \n \tif (show_all) {\n \t\tgit_config(git_help_config, NULL);\n+\t\tif (verbose) {\n+\t\t\tsetup_pager();\n+\t\t\tlist_all_cmds_help();\n+\t\t\treturn 0;\n+\t\t}\n \t\tprintf(_(\"usage: %s%s\"), _(git_usage_string), \"\\n\\n\");\n \t\tload_command_list(\"git-\", &main_cmds, &other_cmds);\n \t\tlist_commands(colopts, &main_cmds, &other_cmds);\ndiff --git a/help.c b/help.c\nindex 1117f7d1d1..c7df1d2338 100644\n--- a/help.c\n+++ b/help.c\n@@ -27,6 +27,17 @@ static struct category_description common_categories[] = {\n \t{ CAT_remote, N_(\"collaborate (see also: git help workflows)\") },\n \t{ 0, NULL }\n };\n+static struct category_description main_categories[] = {\n+\t{ CAT_mainporcelain, N_(\"Main Porcelain Commands\") },\n+\t{ CAT_ancillarymanipulators, N_(\"Ancillary Commands / Manipulators\") },\n+\t{ CAT_ancillaryinterrogators, N_(\"Ancillary Commands / Interrogators\") },\n+\t{ CAT_foreignscminterface, N_(\"Interacting with Others\") },\n+\t{ CAT_plumbingmanipulators, N_(\"Low-level Commands / Manipulators\") },\n+\t{ CAT_plumbinginterrogators, N_(\"Low-level Commands / Interrogators\") },\n+\t{ CAT_synchingrepositories, N_(\"Low-level Commands / Synching Repositories\") },\n+\t{ CAT_purehelpers, N_(\"Low-level Commands / Internal Helpers\") },\n+\t{ 0, NULL }\n+};\n \n static const char *drop_prefix(const char *name)\n {\n@@ -352,6 +363,11 @@ void list_cmds_by_category(struct string_list *list,\n \t}\n }\n \n+void list_all_cmds_help(void)\n+{\n+\tprint_cmd_by_category(main_categories);\n+}\n+\n int is_in_cmdlist(struct cmdnames *c, const char *s)\n {\n \tint i;\ndiff --git a/help.h b/help.h\nindex 734bba32d3..40917fc38c 100644\n--- a/help.h\n+++ b/help.h\n@@ -19,6 +19,8 @@ static inline void mput_char(char c, unsigned int num)\n }\n \n extern void list_common_cmds_help(void);\n+extern void list_all_cmds_help(void);\n+\n extern void list_all_main_cmds(struct string_list *list);\n extern void list_all_other_cmds(struct string_list *list);\n extern void list_cmds_by_category(struct string_list *list,\ndiff --git a/t/t0012-help.sh b/t/t0012-help.sh\nindex 3c91a9024a..060df24c2d 100755\n--- a/t/t0012-help.sh\n+++ b/t/t0012-help.sh\n@@ -25,6 +25,15 @@ test_expect_success \"setup\" '\n \tEOF\n '\n \n+# make sure to exercise these code paths, the output is a bit tricky\n+# to verify\n+test_expect_success 'basic help commands' '\n+\tgit help >/dev/null &&\n+\tgit help -a >/dev/null &&\n+\tgit help -g >/dev/null &&\n+\tgit help -av >/dev/null\n+'\n+\n test_expect_success \"works for commands and guides by default\" '\n \tconfigure_help &&\n \tgit help status &&\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348037","messageId":"20180519042752.8666-9-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 08/14] git: support --list-cmds=list-<category>","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:46Z","receivedAt":"2018-05-19T04:28:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This allows us to select any group of commands by a category defined\nin command-list.txt. This is an internal/hidden option so we don't\nhave to be picky about the category name or worried about exposing too\nmuch.\n\nThis will be used later by git-completion.bash to retrieve certain\ncommand groups.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n generate-cmdlist.sh | 17 +++++++++++++++++\n git.c               |  7 +++++++\n help.c              | 23 +++++++++++++++++++++++\n help.h              |  2 ++\n 4 files changed, 49 insertions(+)\n\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex 3bcc1ee57d..8d6d8b45ce 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -45,6 +45,21 @@ define_categories () {\n \ttest \"$bit\" -gt 32 && die \"Urgh.. too many categories?\"\n }\n \n+define_category_names () {\n+\techo\n+\techo \"/* Category names */\"\n+\techo \"static const char *category_names[] = {\"\n+\tbit=0\n+\tcategory_list \"$1\" |\n+\twhile read cat\n+\tdo\n+\t\techo \"\t\\\"$cat\\\", /* (1UL << $bit) */\"\n+\t\tbit=$(($bit+1))\n+\tdone\n+\techo \"\tNULL\"\n+\techo \"};\"\n+}\n+\n print_command_list () {\n \techo \"static struct cmdname_help command_list[] = {\"\n \n@@ -70,4 +85,6 @@ struct cmdname_help {\n \"\n define_categories \"$1\"\n echo\n+define_category_names \"$1\"\n+echo\n print_command_list \"$1\"\ndiff --git a/git.c b/git.c\nindex 10907f7266..4d5b8a9931 100644\n--- a/git.c\n+++ b/git.c\n@@ -60,6 +60,13 @@ static int list_cmds(const char *spec)\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n \t\t\tlist_all_other_cmds(&list);\n+\t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\t\tstrbuf_add(&sb, spec + 5, len - 5);\n+\t\t\tlist_cmds_by_category(&list, sb.buf);\n+\t\t\tstrbuf_release(&sb);\n+\t\t}\n \t\telse\n \t\t\tdie(_(\"unsupported command listing type '%s'\"), spec);\n \t\tspec += len;\ndiff --git a/help.c b/help.c\nindex d5ce9dfcbb..1117f7d1d1 100644\n--- a/help.c\n+++ b/help.c\n@@ -329,6 +329,29 @@ void list_all_other_cmds(struct string_list *list)\n \tclean_cmdnames(&other_cmds);\n }\n \n+void list_cmds_by_category(struct string_list *list,\n+\t\t\t   const char *cat)\n+{\n+\tint i, n = ARRAY_SIZE(command_list);\n+\tuint32_t cat_id = 0;\n+\n+\tfor (i = 0; category_names[i]; i++) {\n+\t\tif (!strcmp(cat, category_names[i])) {\n+\t\t\tcat_id = 1UL << i;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (!cat_id)\n+\t\tdie(_(\"unsupported command listing type '%s'\"), cat);\n+\n+\tfor (i = 0; i < n; i++) {\n+\t\tstruct cmdname_help *cmd = command_list + i;\n+\n+\t\tif (cmd->category & cat_id)\n+\t\t\tstring_list_append(list, drop_prefix(cmd->name));\n+\t}\n+}\n+\n int is_in_cmdlist(struct cmdnames *c, const char *s)\n {\n \tint i;\ndiff --git a/help.h b/help.h\nindex 97e6c0965e..734bba32d3 100644\n--- a/help.h\n+++ b/help.h\n@@ -21,6 +21,8 @@ static inline void mput_char(char c, unsigned int num)\n extern void list_common_cmds_help(void);\n extern void list_all_main_cmds(struct string_list *list);\n extern void list_all_other_cmds(struct string_list *list);\n+extern void list_cmds_by_category(struct string_list *list,\n+\t\t\t\t  const char *category);\n extern const char *help_unknown_cmd(const char *cmd);\n extern void load_command_list(const char *prefix,\n \t\t\t      struct cmdnames *main_cmds,\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348039","messageId":"20180519042752.8666-6-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 05/14] git.c: convert --list-* to --list-cmds=*","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:43Z","receivedAt":"2018-05-19T04:28:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Even if these are hidden options, let's make them a bit more generic\nsince we're introducing more listing types shortly. The code is\nstructured to allow combining multiple listing types together because\nwe will soon add more types the 'builtins'.\n\n'parseopt' remains separate because it has separate (SPC) to match\ngit-completion.bash needs and will not combine with others.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n contrib/completion/git-completion.bash |  2 +-\n git.c                                  | 37 +++++++++++++++++++++-----\n t/t0012-help.sh                        |  2 +-\n 3 files changed, 33 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex a757073945..3556838759 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -3049,7 +3049,7 @@ __git_complete_common () {\n __git_cmds_with_parseopt_helper=\n __git_support_parseopt_helper () {\n \ttest -n \"$__git_cmds_with_parseopt_helper\" ||\n-\t\t__git_cmds_with_parseopt_helper=\"$(__git --list-parseopt-builtins)\"\n+\t\t__git_cmds_with_parseopt_helper=\"$(__git --list-cmds=parseopt)\"\n \n \tcase \" $__git_cmds_with_parseopt_helper \" in\n \t*\" $1 \"*)\ndiff --git a/git.c b/git.c\nindex 3a89893712..cd85355d81 100644\n--- a/git.c\n+++ b/git.c\n@@ -38,6 +38,30 @@ static int use_pager = -1;\n \n static void list_builtins(unsigned int exclude_option, char sep);\n \n+static int match_token(const char *spec, int len, const char *token)\n+{\n+\tint token_len = strlen(token);\n+\n+\treturn len == token_len && !strncmp(spec, token, token_len);\n+}\n+\n+static int list_cmds(const char *spec)\n+{\n+\twhile (*spec) {\n+\t\tconst char *sep = strchrnul(spec, ',');\n+\t\tint len = sep - spec;\n+\n+\t\tif (match_token(spec, len, \"builtins\"))\n+\t\t\tlist_builtins(0, '\\n');\n+\t\telse\n+\t\t\tdie(_(\"unsupported command listing type '%s'\"), spec);\n+\t\tspec += len;\n+\t\tif (*spec == ',')\n+\t\t\tspec++;\n+\t}\n+\treturn 0;\n+}\n+\n static void commit_pager_choice(void) {\n \tswitch (use_pager) {\n \tcase 0:\n@@ -223,12 +247,13 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\t}\n \t\t\t(*argv)++;\n \t\t\t(*argc)--;\n-\t\t} else if (!strcmp(cmd, \"--list-builtins\")) {\n-\t\t\tlist_builtins(0, '\\n');\n-\t\t\texit(0);\n-\t\t} else if (!strcmp(cmd, \"--list-parseopt-builtins\")) {\n-\t\t\tlist_builtins(NO_PARSEOPT, ' ');\n-\t\t\texit(0);\n+\t\t} else if (skip_prefix(cmd, \"--list-cmds=\", &cmd)) {\n+\t\t\tif (!strcmp(cmd, \"parseopt\")) {\n+\t\t\t\tlist_builtins(NO_PARSEOPT, ' ');\n+\t\t\t\texit(0);\n+\t\t\t} else {\n+\t\t\t\texit(list_cmds(cmd));\n+\t\t\t}\n \t\t} else {\n \t\t\tfprintf(stderr, _(\"unknown option: %s\\n\"), cmd);\n \t\t\tusage(git_usage_string);\ndiff --git a/t/t0012-help.sh b/t/t0012-help.sh\nindex c096f33505..3c91a9024a 100755\n--- a/t/t0012-help.sh\n+++ b/t/t0012-help.sh\n@@ -59,7 +59,7 @@ test_expect_success 'git help' '\n '\n \n test_expect_success 'generate builtin list' '\n-\tgit --list-builtins >builtins\n+\tgit --list-cmds=builtins >builtins\n '\n \n while read builtin\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348038","messageId":"20180519042752.8666-5-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 04/14] Remove common-cmds.h","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:42Z","receivedAt":"2018-05-19T04:28:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"After the last patch, common-cmds.h is no longer used (and it was\nactually broken). Remove all related code. command-list.h will take\nits place from now on.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore          |  1 -\n Makefile            | 17 ++++++-----------\n generate-cmdlist.sh | 46 +++------------------------------------------\n 3 files changed, 9 insertions(+), 55 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex d4c3914167..0836083992 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -179,7 +179,6 @@\n /gitweb/gitweb.cgi\n /gitweb/static/gitweb.js\n /gitweb/static/gitweb.min.*\n-/common-cmds.h\n /command-list.h\n *.tar.gz\n *.dsc\ndiff --git a/Makefile b/Makefile\nindex 5c58b0b692..a60a78ee67 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -757,7 +757,7 @@ LIB_FILE = libgit.a\n XDIFF_LIB = xdiff/lib.a\n VCSSVN_LIB = vcs-svn/lib.a\n \n-GENERATED_H += common-cmds.h command-list.h\n+GENERATED_H += command-list.h\n \n LIB_H = $(shell $(FIND) . \\\n \t-name .git -prune -o \\\n@@ -1914,9 +1914,9 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n \t\t$(filter %.o,$^) $(LIBS)\n \n-help.sp help.s help.o: common-cmds.h command-list.h\n+help.sp help.s help.o: command-list.h\n \n-builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h command-list.h GIT-PREFIX\n+builtin/help.sp builtin/help.s builtin/help.o: command-list.h GIT-PREFIX\n builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \\\n \t'-DGIT_HTML_PATH=\"$(htmldir_relative_SQ)\"' \\\n \t'-DGIT_MAN_PATH=\"$(mandir_relative_SQ)\"' \\\n@@ -1935,11 +1935,6 @@ $(BUILT_INS): git$X\n \tln -s $< $@ 2>/dev/null || \\\n \tcp $< $@\n \n-common-cmds.h: generate-cmdlist.sh command-list.txt\n-\n-common-cmds.h: $(wildcard Documentation/git-*.txt)\n-\t$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt COMMON >$@+ && mv $@+ $@\n-\n command-list.h: generate-cmdlist.sh command-list.txt\n \n command-list.h: $(wildcard Documentation/git-*.txt)\n@@ -2153,7 +2148,7 @@ else\n # Dependencies on header files, for platforms that do not support\n # the gcc -MMD option.\n #\n-# Dependencies on automatically generated headers such as common-cmds.h or command-list.h\n+# Dependencies on automatically generated headers such as command-list.h\n # should _not_ be included here, since they are necessary even when\n # building an object for the first time.\n \n@@ -2532,7 +2527,7 @@ sparse: $(SP_OBJ)\n style:\n \tgit clang-format --style file --diff --extensions c,h\n \n-check: common-cmds.h command-list.h\n+check: command-list.h\n \t@if sparse; \\\n \tthen \\\n \t\techo >&2 \"Use 'make sparse' instead\"; \\\n@@ -2780,7 +2775,7 @@ clean: profile-clean coverage-clean\n \t$(RM) $(TEST_PROGRAMS) $(NO_INSTALL)\n \t$(RM) -r bin-wrappers $(dep_dirs)\n \t$(RM) -r po/build/\n-\t$(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h command-list.h $(ETAGS_TARGET) tags cscope*\n+\t$(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*\n \t$(RM) -r $(GIT_TARNAME) .doc-tmp-dir\n \t$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex 9eb22c4ef1..3bcc1ee57d 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -68,46 +68,6 @@ struct cmdname_help {\n \tuint32_t category;\n };\n \"\n-if test -z \"$2\"\n-then\n-\tdefine_categories \"$1\"\n-\techo\n-\tprint_command_list \"$1\"\n-\texit 0\n-fi\n-\n-echo \"static const char *common_cmd_groups[] = {\"\n-\n-grps=grps$$.tmp\n-match=match$$.tmp\n-trap \"rm -f '$grps' '$match'\" 0 1 2 3 15\n-\n-sed -n '\n-\t1,/^### common groups/b\n-\t/^### command list/q\n-\t/^#/b\n-\t/^[ \t]*$/b\n-\th;s/^[^ \t][^ \t]*[ \t][ \t]*\\(.*\\)/\tN_(\"\\1\"),/p\n-\tg;s/^\\([^ \t][^ \t]*\\)[ \t].*/\\1/w '$grps'\n-\t' \"$1\"\n-printf '};\\n\\n'\n-\n-n=0\n-substnum=\n-while read grp\n-do\n-\techo \"^git-..*[ \t]$grp\"\n-\tsubstnum=\"$substnum${substnum:+;}s/[ \t]$grp/$n/\"\n-\tn=$(($n+1))\n-done <\"$grps\" >\"$match\"\n-\n-printf 'static struct cmdname_help common_cmds[] = {\\n'\n-grep -f \"$match\" \"$1\" |\n-sed 's/^git-//' |\n-sort |\n-while read cmd tags\n-do\n-\ttag=$(echo \"$tags\" | sed \"$substnum; s/[^0-9]//g\")\n-\techo \"\t{\\\"$cmd\\\", $(get_synopsis git-$cmd), $tag},\"\n-done\n-echo \"};\"\n+define_categories \"$1\"\n+echo\n+print_command_list \"$1\"\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348040","messageId":"20180519042752.8666-15-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 14/14] completion: allow to customize the completable command list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:52Z","receivedAt":"2018-05-19T04:28:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"By default we show porcelain, external commands and a couple others\nthat are also popular. If you are not happy with this list, you can\nnow customize it. See the big comment block for details.\n\nPS. perhaps I should make aliases a group too, which makes it possible\nto _not_ complete aliases by omitting this special group in\n$GIT_COMPLETION_CMD_GROUPS\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt               | 10 ++++++++\n contrib/completion/git-completion.bash | 28 +++++++++++++++++++++-\n git.c                                  |  2 ++\n help.c                                 | 33 ++++++++++++++++++++++++++\n help.h                                 |  1 +\n 5 files changed, 73 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2659153cb3..91f7eaed7b 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1343,6 +1343,16 @@ credential.<url>.*::\n credentialCache.ignoreSIGHUP::\n \tTell git-credential-cache--daemon to ignore SIGHUP, instead of quitting.\n \n+completion.commands::\n+\tThis is only used by git-completion.bash to add or remove\n+\tcommands from the complete list. Normally only porcelain\n+\tcommands and a few select others are in the complete list. You\n+\tcan add more commands, separated by space, in this\n+\tvariable. Prefixing the command with '-' will remove it from\n+\tthe existing list.\n++\n+This variable should not be per-repository.\n+\n include::diff-config.txt[]\n \n difftool.<tool>.path::\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex cd1d8e553f..f237eb0ff4 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -38,6 +38,29 @@\n #\n #     When set to \"1\", do not include \"DWIM\" suggestions in git-checkout\n #     completion (e.g., completing \"foo\" when \"origin/foo\" exists).\n+#\n+#   GIT_COMPLETION_CMD_GROUPS\n+#\n+#     When set, \"git --list-cmds=$GIT_COMPLETION_CMD_GROUPS\" will be\n+#     used to get the list of completable commands. The default is\n+#     \"mainporcelain,others,list-complete\" (in English: all porcelain\n+#     commands and external ones are included. Certain non-porcelain\n+#     commands are also marked for completion in command-list.txt).\n+#     You could for example complete all commands with\n+#\n+#         GIT_COMPLETION_CMD_GROUPS=main,others\n+#\n+#     Or you could go with main porcelain only and extra commands in\n+#     the configuration variable completion.commands with\n+#\n+#         GIT_COMPLETION_CMD_GROUPS=mainporcelain,config\n+#\n+#     Or go completely custom group with\n+#\n+#         GIT_COMPLETION_CMD_GROUPS=config\n+#\n+#     Or you could even play with other command categories found in\n+#     command-list.txt.\n \n case \"$COMP_WORDBREAKS\" in\n *:*) : great ;;\n@@ -842,8 +865,11 @@ __git_commands () {\n \t\tif test -n \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n \t\tthen\n \t\t\tprintf \"%s\" \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n+\t\telif test -n \"$GIT_COMPLETION_CMD_GROUPS\"\n+\t\tthen\n+\t\t\tgit --list-cmds=\"$GIT_COMPLETION_CMD_GROUPS\"\n \t\telse\n-\t\t\tgit --list-cmds=list-mainporcelain,others,list-complete\n+\t\t\tgit --list-cmds=list-mainporcelain,others,list-complete,config\n \t\tfi\n \t\t;;\n \tall)\ndiff --git a/git.c b/git.c\nindex 4d5b8a9931..ea4feedd0b 100644\n--- a/git.c\n+++ b/git.c\n@@ -60,6 +60,8 @@ static int list_cmds(const char *spec)\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n \t\t\tlist_all_other_cmds(&list);\n+\t\telse if (match_token(spec, len, \"config\"))\n+\t\t\tlist_cmds_by_config(&list);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \ndiff --git a/help.c b/help.c\nindex 23924dd300..abf87205b2 100644\n--- a/help.c\n+++ b/help.c\n@@ -366,6 +366,39 @@ void list_cmds_by_category(struct string_list *list,\n \t}\n }\n \n+void list_cmds_by_config(struct string_list *list)\n+{\n+\tconst char *cmd_list;\n+\n+\t/*\n+\t * There's no actual repository setup at this point (and even\n+\t * if there is, we don't really care; only global config\n+\t * matters). If we accidentally set up a repository, it's ok\n+\t * too since the caller (git --list-cmds=) should exit shortly\n+\t * anyway.\n+\t */\n+\tif (git_config_get_string_const(\"completion.commands\", &cmd_list))\n+\t\treturn;\n+\n+\tstring_list_sort(list);\n+\tstring_list_remove_duplicates(list, 0);\n+\n+\twhile (*cmd_list) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tconst char *p = strchrnul(cmd_list, ' ');\n+\n+\t\tstrbuf_add(&sb, cmd_list, p - cmd_list);\n+\t\tif (*cmd_list == '-')\n+\t\t\tstring_list_remove(list, cmd_list + 1, 0);\n+\t\telse\n+\t\t\tstring_list_insert(list, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\twhile (*p == ' ')\n+\t\t\tp++;\n+\t\tcmd_list = p;\n+\t}\n+}\n+\n void list_common_guides_help(void)\n {\n \tstruct category_description catdesc[] = {\ndiff --git a/help.h b/help.h\nindex b2293e99be..3b38292a1b 100644\n--- a/help.h\n+++ b/help.h\n@@ -26,6 +26,7 @@ extern void list_all_main_cmds(struct string_list *list);\n extern void list_all_other_cmds(struct string_list *list);\n extern void list_cmds_by_category(struct string_list *list,\n \t\t\t\t  const char *category);\n+extern void list_cmds_by_config(struct string_list *list);\n extern const char *help_unknown_cmd(const char *cmd);\n extern void load_command_list(const char *prefix,\n \t\t\t      struct cmdnames *main_cmds,\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348041","messageId":"20180519042752.8666-14-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 13/14] completion: reduce completable command list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:51Z","receivedAt":"2018-05-19T04:28:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The following commands are removed from the complete list:\n\n- annotate             obsolete, discouraged to use\n- filter-branch        not often used\n- get-tar-commit-id    not often used\n- imap-send            not often used\n- interpreter-trailers not for interactive use\n- p4                   too short and probably not often used (*)\n- svn                  same category as p4 (*)\n- verify-commit        not often used\n\n(*) to be fair, send-email command which is in the same foreignscminterface\ngroup as svn and p4 does get completion, just because it's used by git\nand kernel development. So maybe we should include them.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n command-list.txt | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/command-list.txt b/command-list.txt\nindex dcf1907a54..8462ea475f 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -47,7 +47,7 @@\n # command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\n git-am                                  mainporcelain\n-git-annotate                            ancillaryinterrogators          complete\n+git-annotate                            ancillaryinterrogators\n git-apply                               plumbingmanipulators            complete\n git-archimport                          foreignscminterface\n git-archive                             mainporcelain\n@@ -89,13 +89,13 @@ git-fast-export                         ancillarymanipulators\n git-fast-import                         ancillarymanipulators\n git-fetch                               mainporcelain           remote\n git-fetch-pack                          synchingrepositories\n-git-filter-branch                       ancillarymanipulators           complete\n+git-filter-branch                       ancillarymanipulators\n git-fmt-merge-msg                       purehelpers\n git-for-each-ref                        plumbinginterrogators\n git-format-patch                        mainporcelain\n git-fsck                                ancillaryinterrogators          complete\n git-gc                                  mainporcelain\n-git-get-tar-commit-id                   ancillaryinterrogators          complete\n+git-get-tar-commit-id                   ancillaryinterrogators\n git-grep                                mainporcelain           info\n git-gui                                 mainporcelain\n git-hash-object                         plumbingmanipulators\n@@ -103,11 +103,11 @@ git-help                                ancillaryinterrogators          complete\n git-http-backend                        synchingrepositories\n git-http-fetch                          synchelpers\n git-http-push                           synchelpers\n-git-imap-send                           foreignscminterface             complete\n+git-imap-send                           foreignscminterface\n git-index-pack                          plumbingmanipulators\n git-init                                mainporcelain           init\n git-instaweb                            ancillaryinterrogators          complete\n-git-interpret-trailers                  purehelpers                     complete\n+git-interpret-trailers                  purehelpers\n gitk                                    mainporcelain\n git-log                                 mainporcelain           info\n git-ls-files                            plumbinginterrogators\n@@ -127,7 +127,7 @@ git-mktree                              plumbingmanipulators\n git-mv                                  mainporcelain           worktree\n git-name-rev                            plumbinginterrogators           complete\n git-notes                               mainporcelain\n-git-p4                                  foreignscminterface             complete\n+git-p4                                  foreignscminterface\n git-pack-objects                        plumbingmanipulators\n git-pack-redundant                      plumbinginterrogators\n git-pack-refs                           ancillarymanipulators\n@@ -167,7 +167,7 @@ git-stage                                                               complete\n git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n-git-svn                                 foreignscminterface             complete\n+git-svn                                 foreignscminterface\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\n@@ -178,7 +178,7 @@ git-update-server-info                  synchingrepositories\n git-upload-archive                      synchelpers\n git-upload-pack                         synchelpers\n git-var                                 plumbinginterrogators\n-git-verify-commit                       ancillaryinterrogators          complete\n+git-verify-commit                       ancillaryinterrogators\n git-verify-pack                         plumbinginterrogators\n git-verify-tag                          ancillaryinterrogators\n gitweb                                  ancillaryinterrogators\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348042","messageId":"20180519042752.8666-11-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 10/14] help: use command-list.txt for the source of guides","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:48Z","receivedAt":"2018-05-19T04:28:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The help command currently hard codes the list of guides and their\nsummary in C. Let's move this list to command-list.txt. This lets us\nextract summary lines from Documentation/git*.txt. This also\npotentially lets us list guides in git.txt, but I'll leave that for\nnow.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/gitattributes.txt        |  2 +-\n Documentation/gitmodules.txt           |  2 +-\n Documentation/gitrevisions.txt         |  2 +-\n Makefile                               |  2 +-\n builtin/help.c                         | 32 --------------------------\n command-list.txt                       | 16 +++++++++++++\n contrib/completion/git-completion.bash | 15 ++++++++----\n help.c                                 | 21 +++++++++++++----\n help.h                                 |  1 +\n t/t0012-help.sh                        |  6 +++++\n 10 files changed, 54 insertions(+), 45 deletions(-)\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 1094fe2b5b..083c2f380d 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -3,7 +3,7 @@ gitattributes(5)\n \n NAME\n ----\n-gitattributes - defining attributes per path\n+gitattributes - Defining attributes per path\n \n SYNOPSIS\n --------\ndiff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt\nindex db5d47eb19..4d63def206 100644\n--- a/Documentation/gitmodules.txt\n+++ b/Documentation/gitmodules.txt\n@@ -3,7 +3,7 @@ gitmodules(5)\n \n NAME\n ----\n-gitmodules - defining submodule properties\n+gitmodules - Defining submodule properties\n \n SYNOPSIS\n --------\ndiff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt\nindex 27dec5b91d..1f6cceaefb 100644\n--- a/Documentation/gitrevisions.txt\n+++ b/Documentation/gitrevisions.txt\n@@ -3,7 +3,7 @@ gitrevisions(7)\n \n NAME\n ----\n-gitrevisions - specifying revisions and ranges for Git\n+gitrevisions - Specifying revisions and ranges for Git\n \n SYNOPSIS\n --------\ndiff --git a/Makefile b/Makefile\nindex a60a78ee67..1efb751e46 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1937,7 +1937,7 @@ $(BUILT_INS): git$X\n \n command-list.h: generate-cmdlist.sh command-list.txt\n \n-command-list.h: $(wildcard Documentation/git-*.txt)\n+command-list.h: $(wildcard Documentation/git*.txt)\n \t$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@\n \n SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\\\ndiff --git a/builtin/help.c b/builtin/help.c\nindex 0e0af8426a..5727fb5e51 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -402,38 +402,6 @@ static void show_html_page(const char *git_cmd)\n \topen_html(page_path.buf);\n }\n \n-static struct {\n-\tconst char *name;\n-\tconst char *help;\n-} common_guides[] = {\n-\t{ \"attributes\", N_(\"Defining attributes per path\") },\n-\t{ \"everyday\", N_(\"Everyday Git With 20 Commands Or So\") },\n-\t{ \"glossary\", N_(\"A Git glossary\") },\n-\t{ \"ignore\", N_(\"Specifies intentionally untracked files to ignore\") },\n-\t{ \"modules\", N_(\"Defining submodule properties\") },\n-\t{ \"revisions\", N_(\"Specifying revisions and ranges for Git\") },\n-\t{ \"tutorial\", N_(\"A tutorial introduction to Git (for version 1.5.1 or newer)\") },\n-\t{ \"workflows\", N_(\"An overview of recommended workflows with Git\") },\n-};\n-\n-static void list_common_guides_help(void)\n-{\n-\tint i, longest = 0;\n-\n-\tfor (i = 0; i < ARRAY_SIZE(common_guides); i++) {\n-\t\tif (longest < strlen(common_guides[i].name))\n-\t\t\tlongest = strlen(common_guides[i].name);\n-\t}\n-\n-\tputs(_(\"The common Git guides are:\\n\"));\n-\tfor (i = 0; i < ARRAY_SIZE(common_guides); i++) {\n-\t\tprintf(\"   %s   \", common_guides[i].name);\n-\t\tmput_char(' ', longest - strlen(common_guides[i].name));\n-\t\tputs(_(common_guides[i].help));\n-\t}\n-\tputchar('\\n');\n-}\n-\n static const char *check_git_cmd(const char* cmd)\n {\n \tchar *alias;\ndiff --git a/command-list.txt b/command-list.txt\nindex 3bd23201a6..99ddc231c1 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -139,3 +139,19 @@ gitweb                                  ancillaryinterrogators\n git-whatchanged                         ancillaryinterrogators\n git-worktree                            mainporcelain\n git-write-tree                          plumbingmanipulators\n+gitattributes                           guide\n+gitcli                                  guide\n+gitcore-tutorial                        guide\n+gitcvs-migration                        guide\n+gitdiffcore                             guide\n+giteveryday                             guide\n+gitglossary                             guide\n+githooks                                guide\n+gitignore                               guide\n+gitmodules                              guide\n+gitnamespaces                           guide\n+gitrepository-layout                    guide\n+gitrevisions                            guide\n+gittutorial-2                           guide\n+gittutorial                             guide\n+gitworkflows                            guide\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 62ca8641f4..4e724a5b76 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1575,6 +1575,13 @@ _git_grep ()\n \t__git_complete_refs\n }\n \n+__git_all_guides=\n+__git_compute_all_guides ()\n+{\n+\ttest -n \"$__git_all_guides\" ||\n+\t__git_all_guides=$(git --list-cmds=list-guide)\n+}\n+\n _git_help ()\n {\n \tcase \"$cur\" in\n@@ -1584,11 +1591,9 @@ _git_help ()\n \t\t;;\n \tesac\n \t__git_compute_all_commands\n-\t__gitcomp \"$__git_all_commands $(__git_aliases)\n-\t\tattributes cli core-tutorial cvs-migration\n-\t\tdiffcore everyday gitk glossary hooks ignore modules\n-\t\tnamespaces repository-layout revisions tutorial tutorial-2\n-\t\tworkflows\n+\t__git_compute_all_guides\n+\t__gitcomp \"$__git_all_commands $(__git_aliases) $__git_all_guides\n+\t\tgitk\n \t\t\"\n }\n \ndiff --git a/help.c b/help.c\nindex c7df1d2338..23924dd300 100644\n--- a/help.c\n+++ b/help.c\n@@ -39,12 +39,14 @@ static struct category_description main_categories[] = {\n \t{ 0, NULL }\n };\n \n-static const char *drop_prefix(const char *name)\n+static const char *drop_prefix(const char *name, uint32_t category)\n {\n \tconst char *new_name;\n \n \tif (skip_prefix(name, \"git-\", &new_name))\n \t\treturn new_name;\n+\tif (category == CAT_guide && skip_prefix(name, \"git\", &new_name))\n+\t\treturn new_name;\n \treturn name;\n \n }\n@@ -66,7 +68,7 @@ static void extract_cmds(struct cmdname_help **p_cmds, uint32_t mask)\n \t\t\tcontinue;\n \n \t\tcmds[nr] = *cmd;\n-\t\tcmds[nr].name = drop_prefix(cmd->name);\n+\t\tcmds[nr].name = drop_prefix(cmd->name, cmd->category);\n \n \t\tnr++;\n \t}\n@@ -358,11 +360,22 @@ void list_cmds_by_category(struct string_list *list,\n \tfor (i = 0; i < n; i++) {\n \t\tstruct cmdname_help *cmd = command_list + i;\n \n-\t\tif (cmd->category & cat_id)\n-\t\t\tstring_list_append(list, drop_prefix(cmd->name));\n+\t\tif (!(cmd->category & cat_id))\n+\t\t\tcontinue;\n+\t\tstring_list_append(list, drop_prefix(cmd->name, cmd->category));\n \t}\n }\n \n+void list_common_guides_help(void)\n+{\n+\tstruct category_description catdesc[] = {\n+\t\t{ CAT_guide, N_(\"The common Git guides are:\") },\n+\t\t{ 0, NULL }\n+\t};\n+\tprint_cmd_by_category(catdesc);\n+\tputchar('\\n');\n+}\n+\n void list_all_cmds_help(void)\n {\n \tprint_cmd_by_category(main_categories);\ndiff --git a/help.h b/help.h\nindex 40917fc38c..b2293e99be 100644\n--- a/help.h\n+++ b/help.h\n@@ -20,6 +20,7 @@ static inline void mput_char(char c, unsigned int num)\n \n extern void list_common_cmds_help(void);\n extern void list_all_cmds_help(void);\n+extern void list_common_guides_help(void);\n \n extern void list_all_main_cmds(struct string_list *list);\n extern void list_all_other_cmds(struct string_list *list);\ndiff --git a/t/t0012-help.sh b/t/t0012-help.sh\nindex 060df24c2d..bc27df7f38 100755\n--- a/t/t0012-help.sh\n+++ b/t/t0012-help.sh\n@@ -66,6 +66,12 @@ test_expect_success 'git help' '\n \ttest_i18ngrep \"^   commit \" help.output &&\n \ttest_i18ngrep \"^   fetch  \" help.output\n '\n+test_expect_success 'git help -g' '\n+\tgit help -g >help.output &&\n+\ttest_i18ngrep \"^   attributes \" help.output &&\n+\ttest_i18ngrep \"^   everyday   \" help.output &&\n+\ttest_i18ngrep \"^   tutorial   \" help.output\n+'\n \n test_expect_success 'generate builtin list' '\n \tgit --list-cmds=builtins >builtins\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348043","messageId":"20180519042752.8666-4-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 03/14] help: use command-list.h for common command list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:41Z","receivedAt":"2018-05-19T04:28:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The previous commit added code generation for all_cmd_desc[] which\nincludes almost everything we need to generate common command list.\nConvert help code to use that array instead and drop common_cmds[] array.\n\nThe description of each common command group is removed from\ncommand-list.txt. This keeps this file format simpler. common-cmds.h\nwill not be generated correctly after this change due to the\ncommand-list.txt format change. But it does not matter and\ncommon-cmds.h will be removed.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile            |   4 +-\n command-list.txt    |  10 ---\n generate-cmdlist.sh |   4 +-\n help.c              | 145 +++++++++++++++++++++++++++++++++-----------\n t/t0012-help.sh     |   9 +++\n 5 files changed, 122 insertions(+), 50 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 2a8913ea21..5c58b0b692 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1914,9 +1914,9 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n \t\t$(filter %.o,$^) $(LIBS)\n \n-help.sp help.s help.o: common-cmds.h\n+help.sp help.s help.o: common-cmds.h command-list.h\n \n-builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h GIT-PREFIX\n+builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h command-list.h GIT-PREFIX\n builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \\\n \t'-DGIT_HTML_PATH=\"$(htmldir_relative_SQ)\"' \\\n \t'-DGIT_MAN_PATH=\"$(mandir_relative_SQ)\"' \\\ndiff --git a/command-list.txt b/command-list.txt\nindex 786536aba0..3bd23201a6 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -1,13 +1,3 @@\n-# common commands are grouped by themes\n-# these groups are output by 'git help' in the order declared here.\n-# map each common command in the command list to one of these groups.\n-### common groups (do not change this line)\n-init         start a working area (see also: git help tutorial)\n-worktree     work on the current change (see also: git help everyday)\n-info         examine the history and state (see also: git help revisions)\n-history      grow, mark and tweak your common history\n-remote       collaborate (see also: git help workflows)\n-\n ### command list (do not change this line, also do not change alignment)\n # command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex 870d3b626a..9eb22c4ef1 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -6,7 +6,7 @@ die () {\n }\n \n command_list () {\n-\tsed '1,/^### command list/d;/^#/d' \"$1\"\n+\tgrep -v '^#' \"$1\"\n }\n \n get_categories () {\n@@ -65,7 +65,7 @@ echo \"/* Automatically generated by generate-cmdlist.sh */\n struct cmdname_help {\n \tconst char *name;\n \tconst char *help;\n-\tuint32_t group;\n+\tuint32_t category;\n };\n \"\n if test -z \"$2\"\ndiff --git a/help.c b/help.c\nindex 60071a9bea..2d6a3157f8 100644\n--- a/help.c\n+++ b/help.c\n@@ -5,13 +5,114 @@\n #include \"run-command.h\"\n #include \"levenshtein.h\"\n #include \"help.h\"\n-#include \"common-cmds.h\"\n+#include \"command-list.h\"\n #include \"string-list.h\"\n #include \"column.h\"\n #include \"version.h\"\n #include \"refs.h\"\n #include \"parse-options.h\"\n \n+struct category_description {\n+\tuint32_t category;\n+\tconst char *desc;\n+};\n+static uint32_t common_mask =\n+\tCAT_init | CAT_worktree | CAT_info |\n+\tCAT_history | CAT_remote;\n+static struct category_description common_categories[] = {\n+\t{ CAT_init, N_(\"start a working area (see also: git help tutorial)\") },\n+\t{ CAT_worktree, N_(\"work on the current change (see also: git help everyday)\") },\n+\t{ CAT_info, N_(\"examine the history and state (see also: git help revisions)\") },\n+\t{ CAT_history, N_(\"grow, mark and tweak your common history\") },\n+\t{ CAT_remote, N_(\"collaborate (see also: git help workflows)\") },\n+\t{ 0, NULL }\n+};\n+\n+static const char *drop_prefix(const char *name)\n+{\n+\tconst char *new_name;\n+\n+\tif (skip_prefix(name, \"git-\", &new_name))\n+\t\treturn new_name;\n+\treturn name;\n+\n+}\n+\n+static void extract_cmds(struct cmdname_help **p_cmds, uint32_t mask)\n+{\n+\tint i, nr = 0;\n+\tstruct cmdname_help *cmds;\n+\n+\tif (ARRAY_SIZE(command_list) == 0)\n+\t\tBUG(\"empty command_list[] is a sign of broken generate-cmdlist.sh\");\n+\n+\tALLOC_ARRAY(cmds, ARRAY_SIZE(command_list) + 1);\n+\n+\tfor (i = 0; i < ARRAY_SIZE(command_list); i++) {\n+\t\tconst struct cmdname_help *cmd = command_list + i;\n+\n+\t\tif (!(cmd->category & mask))\n+\t\t\tcontinue;\n+\n+\t\tcmds[nr] = *cmd;\n+\t\tcmds[nr].name = drop_prefix(cmd->name);\n+\n+\t\tnr++;\n+\t}\n+\tcmds[nr].name = NULL;\n+\t*p_cmds = cmds;\n+}\n+\n+static void print_command_list(const struct cmdname_help *cmds,\n+\t\t\t       uint32_t mask, int longest)\n+{\n+\tint i;\n+\n+\tfor (i = 0; cmds[i].name; i++) {\n+\t\tif (cmds[i].category & mask) {\n+\t\t\tprintf(\"   %s   \", cmds[i].name);\n+\t\t\tmput_char(' ', longest - strlen(cmds[i].name));\n+\t\t\tputs(_(cmds[i].help));\n+\t\t}\n+\t}\n+}\n+\n+static int cmd_name_cmp(const void *elem1, const void *elem2)\n+{\n+\tconst struct cmdname_help *e1 = elem1;\n+\tconst struct cmdname_help *e2 = elem2;\n+\n+\treturn strcmp(e1->name, e2->name);\n+}\n+\n+static void print_cmd_by_category(const struct category_description *catdesc)\n+{\n+\tstruct cmdname_help *cmds;\n+\tint longest = 0;\n+\tint i, nr = 0;\n+\tuint32_t mask = 0;\n+\n+\tfor (i = 0; catdesc[i].desc; i++)\n+\t\tmask |= catdesc[i].category;\n+\n+\textract_cmds(&cmds, mask);\n+\n+\tfor (i = 0; cmds[i].name; i++, nr++) {\n+\t\tif (longest < strlen(cmds[i].name))\n+\t\t\tlongest = strlen(cmds[i].name);\n+\t}\n+\tQSORT(cmds, nr, cmd_name_cmp);\n+\n+\tfor (i = 0; catdesc[i].desc; i++) {\n+\t\tuint32_t mask = catdesc[i].category;\n+\t\tconst char *desc = catdesc[i].desc;\n+\n+\t\tprintf(\"\\n%s\\n\", _(desc));\n+\t\tprint_command_list(cmds, mask, longest);\n+\t}\n+\tfree(cmds);\n+}\n+\n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\n \tstruct cmdname *ent;\n@@ -190,42 +291,10 @@ void list_commands(unsigned int colopts,\n \t}\n }\n \n-static int cmd_group_cmp(const void *elem1, const void *elem2)\n-{\n-\tconst struct cmdname_help *e1 = elem1;\n-\tconst struct cmdname_help *e2 = elem2;\n-\n-\tif (e1->group < e2->group)\n-\t\treturn -1;\n-\tif (e1->group > e2->group)\n-\t\treturn 1;\n-\treturn strcmp(e1->name, e2->name);\n-}\n-\n void list_common_cmds_help(void)\n {\n-\tint i, longest = 0;\n-\tint current_grp = -1;\n-\n-\tfor (i = 0; i < ARRAY_SIZE(common_cmds); i++) {\n-\t\tif (longest < strlen(common_cmds[i].name))\n-\t\t\tlongest = strlen(common_cmds[i].name);\n-\t}\n-\n-\tQSORT(common_cmds, ARRAY_SIZE(common_cmds), cmd_group_cmp);\n-\n \tputs(_(\"These are common Git commands used in various situations:\"));\n-\n-\tfor (i = 0; i < ARRAY_SIZE(common_cmds); i++) {\n-\t\tif (common_cmds[i].group != current_grp) {\n-\t\t\tprintf(\"\\n%s\\n\", _(common_cmd_groups[common_cmds[i].group]));\n-\t\t\tcurrent_grp = common_cmds[i].group;\n-\t\t}\n-\n-\t\tprintf(\"   %s   \", common_cmds[i].name);\n-\t\tmput_char(' ', longest - strlen(common_cmds[i].name));\n-\t\tputs(_(common_cmds[i].help));\n-\t}\n+\tprint_cmd_by_category(common_categories);\n }\n \n int is_in_cmdlist(struct cmdnames *c, const char *s)\n@@ -285,6 +354,7 @@ const char *help_unknown_cmd(const char *cmd)\n {\n \tint i, n, best_similarity = 0;\n \tstruct cmdnames main_cmds, other_cmds;\n+\tstruct cmdname_help *common_cmds;\n \n \tmemset(&main_cmds, 0, sizeof(main_cmds));\n \tmemset(&other_cmds, 0, sizeof(other_cmds));\n@@ -299,6 +369,8 @@ const char *help_unknown_cmd(const char *cmd)\n \tQSORT(main_cmds.names, main_cmds.cnt, cmdname_compare);\n \tuniq(&main_cmds);\n \n+\textract_cmds(&common_cmds, common_mask);\n+\n \t/* This abuses cmdname->len for levenshtein distance */\n \tfor (i = 0, n = 0; i < main_cmds.cnt; i++) {\n \t\tint cmp = 0; /* avoid compiler stupidity */\n@@ -313,10 +385,10 @@ const char *help_unknown_cmd(const char *cmd)\n \t\t\tdie(_(bad_interpreter_advice), cmd, cmd);\n \n \t\t/* Does the candidate appear in common_cmds list? */\n-\t\twhile (n < ARRAY_SIZE(common_cmds) &&\n+\t\twhile (common_cmds[n].name &&\n \t\t       (cmp = strcmp(common_cmds[n].name, candidate)) < 0)\n \t\t\tn++;\n-\t\tif ((n < ARRAY_SIZE(common_cmds)) && !cmp) {\n+\t\tif (common_cmds[n].name && !cmp) {\n \t\t\t/* Yes, this is one of the common commands */\n \t\t\tn++; /* use the entry from common_cmds[] */\n \t\t\tif (starts_with(candidate, cmd)) {\n@@ -329,6 +401,7 @@ const char *help_unknown_cmd(const char *cmd)\n \t\tmain_cmds.names[i]->len =\n \t\t\tlevenshtein(cmd, candidate, 0, 2, 1, 3) + 1;\n \t}\n+\tFREE_AND_NULL(common_cmds);\n \n \tQSORT(main_cmds.names, main_cmds.cnt, levenshtein_compare);\n \ndiff --git a/t/t0012-help.sh b/t/t0012-help.sh\nindex 487b92a5de..c096f33505 100755\n--- a/t/t0012-help.sh\n+++ b/t/t0012-help.sh\n@@ -49,6 +49,15 @@ test_expect_success \"--help does not work for guides\" \"\n \ttest_i18ncmp expect actual\n \"\n \n+test_expect_success 'git help' '\n+\tgit help >help.output &&\n+\ttest_i18ngrep \"^   clone  \" help.output &&\n+\ttest_i18ngrep \"^   add    \" help.output &&\n+\ttest_i18ngrep \"^   log    \" help.output &&\n+\ttest_i18ngrep \"^   commit \" help.output &&\n+\ttest_i18ngrep \"^   fetch  \" help.output\n+'\n+\n test_expect_success 'generate builtin list' '\n \tgit --list-builtins >builtins\n '\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348044","messageId":"20180519042752.8666-12-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 11/14] command-list.txt: documentation and guide line","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:49Z","receivedAt":"2018-05-19T04:28:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is intended to help anybody who needs to update command-list.txt.\nIt gives a brief introduction of all attributes a command can take.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n command-list.txt | 45 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 45 insertions(+)\n\ndiff --git a/command-list.txt b/command-list.txt\nindex 99ddc231c1..a2f360eab9 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -1,3 +1,48 @@\n+# Command classification list\n+# ---------------------------\n+# All supported commands, builtin or external, must be described in\n+# here. This info is used to list commands in various places. Each\n+# command is on one line followed by one or more attributes.\n+#\n+# The first attribute group is mandatory and indicates the command\n+# type. This group includes:\n+#\n+#   mainporcelain\n+#   ancillarymanipulators\n+#   ancillaryinterrogators\n+#   foreignscminterface\n+#   plumbingmanipulators\n+#   plumbinginterrogators\n+#   synchingrepositories\n+#   synchelpers\n+#   purehelpers\n+#\n+# The type names are self explanatory. But if you want to see what\n+# command belongs to what group to get a better picture, have a look\n+# at \"git\" man page, \"GIT COMMANDS\" section.\n+#\n+# Commands of type mainporcelain can also optionally have one of these\n+# attributes:\n+#\n+#   init\n+#   worktree\n+#   info\n+#   history\n+#   remote\n+#\n+# These commands are considered \"common\" and will show up in \"git\n+# help\" output in groups. Uncommon porcelain commands must not\n+# specify any of these attributes.\n+#\n+# \"complete\" attribute is used to mark that the command should be\n+# completable by git-completion.bash. Note that by default,\n+# mainporcelain commands are completable so you don't need this\n+# attribute.\n+#\n+# As part of the Git man page list, the man(5/7) guides are also\n+# specified here, which can only have \"guide\" attribute and nothing\n+# else.\n+#\n ### command list (do not change this line, also do not change alignment)\n # command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348045","messageId":"20180519042752.8666-13-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH 12/14] completion: let git provide the completable command list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-19T04:27:50Z","receivedAt":"2018-05-19T04:28:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Instead of maintaining a separate list of command classification,\nwhich often could go out of date, let's centralize the information\nback in git.\n\nWhile the function in git-completion.bash implies \"list porcelain\ncommands\", that's not exactly what it does. It gets all commands (aka\n--list-cmds=main,others) then exclude certain non-porcelain ones. We\ncould almost recreate this list two lists list-mainporcelain and\nothers. The non-porcelain-but-included-anyway is added by the third\ncategory list-complete.\n\nlist-complete does not recreate exactly the command list before this\npatch though. The following commands will disappear from the complete\nlist because they are not in command-list.txt and it's not worth\nadding them back: lost-found, peek-remote and tar-tree.\n\nNote that the current completion script incorrectly classifies\nfilter-branch as porcelain and t9902 tests this behavior. We keep it\nthis way in t9902 because this test does not really care which\nparticular command is porcelain or plubmbing, they're just names.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n command-list.txt                       |  53 +++++------\n contrib/completion/git-completion.bash | 119 ++++++-------------------\n t/t9902-completion.sh                  |   5 +-\n 3 files changed, 58 insertions(+), 119 deletions(-)\n\ndiff --git a/command-list.txt b/command-list.txt\nindex a2f360eab9..dcf1907a54 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -47,12 +47,12 @@\n # command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\n git-am                                  mainporcelain\n-git-annotate                            ancillaryinterrogators\n-git-apply                               plumbingmanipulators\n+git-annotate                            ancillaryinterrogators          complete\n+git-apply                               plumbingmanipulators            complete\n git-archimport                          foreignscminterface\n git-archive                             mainporcelain\n git-bisect                              mainporcelain           info\n-git-blame                               ancillaryinterrogators\n+git-blame                               ancillaryinterrogators          complete\n git-branch                              mainporcelain           history\n git-bundle                              mainporcelain\n git-cat-file                            plumbinginterrogators\n@@ -62,7 +62,7 @@ git-check-mailmap                       purehelpers\n git-checkout                            mainporcelain           history\n git-checkout-index                      plumbingmanipulators\n git-check-ref-format                    purehelpers\n-git-cherry                              ancillaryinterrogators\n+git-cherry                              ancillaryinterrogators          complete\n git-cherry-pick                         mainporcelain\n git-citool                              mainporcelain\n git-clean                               mainporcelain\n@@ -70,7 +70,7 @@ git-clone                               mainporcelain           init\n git-column                              purehelpers\n git-commit                              mainporcelain           history\n git-commit-tree                         plumbingmanipulators\n-git-config                              ancillarymanipulators\n+git-config                              ancillarymanipulators           complete\n git-count-objects                       ancillaryinterrogators\n git-credential                          purehelpers\n git-credential-cache                    purehelpers\n@@ -84,30 +84,30 @@ git-diff                                mainporcelain           history\n git-diff-files                          plumbinginterrogators\n git-diff-index                          plumbinginterrogators\n git-diff-tree                           plumbinginterrogators\n-git-difftool                            ancillaryinterrogators\n+git-difftool                            ancillaryinterrogators          complete\n git-fast-export                         ancillarymanipulators\n git-fast-import                         ancillarymanipulators\n git-fetch                               mainporcelain           remote\n git-fetch-pack                          synchingrepositories\n-git-filter-branch                       ancillarymanipulators\n+git-filter-branch                       ancillarymanipulators           complete\n git-fmt-merge-msg                       purehelpers\n git-for-each-ref                        plumbinginterrogators\n git-format-patch                        mainporcelain\n-git-fsck                                ancillaryinterrogators\n+git-fsck                                ancillaryinterrogators          complete\n git-gc                                  mainporcelain\n-git-get-tar-commit-id                   ancillaryinterrogators\n+git-get-tar-commit-id                   ancillaryinterrogators          complete\n git-grep                                mainporcelain           info\n git-gui                                 mainporcelain\n git-hash-object                         plumbingmanipulators\n-git-help                                ancillaryinterrogators\n+git-help                                ancillaryinterrogators          complete\n git-http-backend                        synchingrepositories\n git-http-fetch                          synchelpers\n git-http-push                           synchelpers\n-git-imap-send                           foreignscminterface\n+git-imap-send                           foreignscminterface             complete\n git-index-pack                          plumbingmanipulators\n git-init                                mainporcelain           init\n-git-instaweb                            ancillaryinterrogators\n-git-interpret-trailers                  purehelpers\n+git-instaweb                            ancillaryinterrogators          complete\n+git-interpret-trailers                  purehelpers                     complete\n gitk                                    mainporcelain\n git-log                                 mainporcelain           info\n git-ls-files                            plumbinginterrogators\n@@ -120,14 +120,14 @@ git-merge-base                          plumbinginterrogators\n git-merge-file                          plumbingmanipulators\n git-merge-index                         plumbingmanipulators\n git-merge-one-file                      purehelpers\n-git-mergetool                           ancillarymanipulators\n+git-mergetool                           ancillarymanipulators           complete\n git-merge-tree                          ancillaryinterrogators\n git-mktag                               plumbingmanipulators\n git-mktree                              plumbingmanipulators\n git-mv                                  mainporcelain           worktree\n-git-name-rev                            plumbinginterrogators\n+git-name-rev                            plumbinginterrogators           complete\n git-notes                               mainporcelain\n-git-p4                                  foreignscminterface\n+git-p4                                  foreignscminterface             complete\n git-pack-objects                        plumbingmanipulators\n git-pack-redundant                      plumbinginterrogators\n git-pack-refs                           ancillarymanipulators\n@@ -141,32 +141,33 @@ git-quiltimport                         foreignscminterface\n git-read-tree                           plumbingmanipulators\n git-rebase                              mainporcelain           history\n git-receive-pack                        synchelpers\n-git-reflog                              ancillarymanipulators\n-git-remote                              ancillarymanipulators\n-git-repack                              ancillarymanipulators\n-git-replace                             ancillarymanipulators\n-git-request-pull                        foreignscminterface\n+git-reflog                              ancillarymanipulators           complete\n+git-remote                              ancillarymanipulators           complete\n+git-repack                              ancillarymanipulators           complete\n+git-replace                             ancillarymanipulators           complete\n+git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           worktree\n git-revert                              mainporcelain\n git-rev-list                            plumbinginterrogators\n git-rev-parse                           ancillaryinterrogators\n git-rm                                  mainporcelain           worktree\n-git-send-email                          foreignscminterface\n+git-send-email                          foreignscminterface             complete\n git-send-pack                           synchingrepositories\n git-shell                               synchelpers\n git-shortlog                            mainporcelain\n git-show                                mainporcelain           info\n-git-show-branch                         ancillaryinterrogators\n+git-show-branch                         ancillaryinterrogators          complete\n git-show-index                          plumbinginterrogators\n git-show-ref                            plumbinginterrogators\n git-sh-i18n                             purehelpers\n git-sh-setup                            purehelpers\n git-stash                               mainporcelain\n+git-stage                                                               complete\n git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n-git-svn                                 foreignscminterface\n+git-svn                                 foreignscminterface             complete\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\n@@ -177,11 +178,11 @@ git-update-server-info                  synchingrepositories\n git-upload-archive                      synchelpers\n git-upload-pack                         synchelpers\n git-var                                 plumbinginterrogators\n-git-verify-commit                       ancillaryinterrogators\n+git-verify-commit                       ancillaryinterrogators          complete\n git-verify-pack                         plumbinginterrogators\n git-verify-tag                          ancillaryinterrogators\n gitweb                                  ancillaryinterrogators\n-git-whatchanged                         ancillaryinterrogators\n+git-whatchanged                         ancillaryinterrogators          complete\n git-worktree                            mainporcelain\n git-write-tree                          plumbingmanipulators\n gitattributes                           guide\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 4e724a5b76..cd1d8e553f 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -834,19 +834,33 @@ __git_complete_strategy ()\n \treturn 1\n }\n \n+# __git_commands requires 1 argument:\n+# 1: the command group, either \"all\" or \"porcelain\"\n __git_commands () {\n-\tif test -n \"${GIT_TESTING_COMMAND_COMPLETION:-}\"\n-\tthen\n-\t\tprintf \"%s\" \"${GIT_TESTING_COMMAND_COMPLETION}\"\n-\telse\n-\t\tgit --list-cmds=main,others\n-\tfi\n+\tcase \"$1\" in\n+\tporcelain)\n+\t\tif test -n \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n+\t\tthen\n+\t\t\tprintf \"%s\" \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n+\t\telse\n+\t\t\tgit --list-cmds=list-mainporcelain,others,list-complete\n+\t\tfi\n+\t\t;;\n+\tall)\n+\t\tif test -n \"$GIT_TESTING_ALL_COMMAND_LIST\"\n+\t\tthen\n+\t\t\tprintf \"%s\" \"$GIT_TESTING_ALL_COMMAND_LIST\"\n+\t\telse\n+\t\t\tgit --list-cmds=main,others\n+\t\tfi\n+\t\t;;\n+\tesac\n }\n \n-__git_list_all_commands ()\n+__git_list_commands ()\n {\n \tlocal i IFS=\" \"$'\\n'\n-\tfor i in $(__git_commands)\n+\tfor i in $(__git_commands $1)\n \tdo\n \t\tcase $i in\n \t\t*--*)             : helper pattern;;\n@@ -855,6 +869,11 @@ __git_list_all_commands ()\n \tdone\n }\n \n+__git_list_all_commands ()\n+{\n+\t__git_list_commands all\n+}\n+\n __git_all_commands=\n __git_compute_all_commands ()\n {\n@@ -864,89 +883,7 @@ __git_compute_all_commands ()\n \n __git_list_porcelain_commands ()\n {\n-\tlocal i IFS=\" \"$'\\n'\n-\t__git_compute_all_commands\n-\tfor i in $__git_all_commands\n-\tdo\n-\t\tcase $i in\n-\t\t*--*)             : helper pattern;;\n-\t\tapplymbox)        : ask gittus;;\n-\t\tapplypatch)       : ask gittus;;\n-\t\tarchimport)       : import;;\n-\t\tcat-file)         : plumbing;;\n-\t\tcheck-attr)       : plumbing;;\n-\t\tcheck-ignore)     : plumbing;;\n-\t\tcheck-mailmap)    : plumbing;;\n-\t\tcheck-ref-format) : plumbing;;\n-\t\tcheckout-index)   : plumbing;;\n-\t\tcolumn)           : internal helper;;\n-\t\tcommit-tree)      : plumbing;;\n-\t\tcount-objects)    : infrequent;;\n-\t\tcredential)       : credentials;;\n-\t\tcredential-*)     : credentials helper;;\n-\t\tcvsexportcommit)  : export;;\n-\t\tcvsimport)        : import;;\n-\t\tcvsserver)        : daemon;;\n-\t\tdaemon)           : daemon;;\n-\t\tdiff-files)       : plumbing;;\n-\t\tdiff-index)       : plumbing;;\n-\t\tdiff-tree)        : plumbing;;\n-\t\tfast-import)      : import;;\n-\t\tfast-export)      : export;;\n-\t\tfsck-objects)     : plumbing;;\n-\t\tfetch-pack)       : plumbing;;\n-\t\tfmt-merge-msg)    : plumbing;;\n-\t\tfor-each-ref)     : plumbing;;\n-\t\thash-object)      : plumbing;;\n-\t\thttp-*)           : transport;;\n-\t\tindex-pack)       : plumbing;;\n-\t\tinit-db)          : deprecated;;\n-\t\tlocal-fetch)      : plumbing;;\n-\t\tls-files)         : plumbing;;\n-\t\tls-remote)        : plumbing;;\n-\t\tls-tree)          : plumbing;;\n-\t\tmailinfo)         : plumbing;;\n-\t\tmailsplit)        : plumbing;;\n-\t\tmerge-*)          : plumbing;;\n-\t\tmktree)           : plumbing;;\n-\t\tmktag)            : plumbing;;\n-\t\tpack-objects)     : plumbing;;\n-\t\tpack-redundant)   : plumbing;;\n-\t\tpack-refs)        : plumbing;;\n-\t\tparse-remote)     : plumbing;;\n-\t\tpatch-id)         : plumbing;;\n-\t\tprune)            : plumbing;;\n-\t\tprune-packed)     : plumbing;;\n-\t\tquiltimport)      : import;;\n-\t\tread-tree)        : plumbing;;\n-\t\treceive-pack)     : plumbing;;\n-\t\tremote-*)         : transport;;\n-\t\trerere)           : plumbing;;\n-\t\trev-list)         : plumbing;;\n-\t\trev-parse)        : plumbing;;\n-\t\trunstatus)        : plumbing;;\n-\t\tsh-setup)         : internal;;\n-\t\tshell)            : daemon;;\n-\t\tshow-ref)         : plumbing;;\n-\t\tsend-pack)        : plumbing;;\n-\t\tshow-index)       : plumbing;;\n-\t\tssh-*)            : transport;;\n-\t\tstripspace)       : plumbing;;\n-\t\tsymbolic-ref)     : plumbing;;\n-\t\tunpack-file)      : plumbing;;\n-\t\tunpack-objects)   : plumbing;;\n-\t\tupdate-index)     : plumbing;;\n-\t\tupdate-ref)       : plumbing;;\n-\t\tupdate-server-info) : daemon;;\n-\t\tupload-archive)   : plumbing;;\n-\t\tupload-pack)      : plumbing;;\n-\t\twrite-tree)       : plumbing;;\n-\t\tvar)              : infrequent;;\n-\t\tverify-pack)      : infrequent;;\n-\t\tverify-tag)       : plumbing;;\n-\t\t*) echo $i;;\n-\t\tesac\n-\tdone\n+\t__git_list_commands porcelain\n }\n \n __git_porcelain_commands=\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 1b34caa1e1..2f16679380 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -13,7 +13,7 @@ complete ()\n \treturn 0\n }\n \n-# Be careful when updating this list:\n+# Be careful when updating these lists:\n #\n # (1) The build tree may have build artifact from different branch, or\n #     the user's $PATH may have a random executable that may begin\n@@ -30,7 +30,8 @@ complete ()\n #     completion for \"git <TAB>\", and a plumbing is excluded.  \"add\",\n #     \"filter-branch\" and \"ls-files\" are listed for this.\n \n-GIT_TESTING_COMMAND_COMPLETION='add checkout check-attr filter-branch ls-files'\n+GIT_TESTING_ALL_COMMAND_LIST='add checkout check-attr filter-branch ls-files'\n+GIT_TESTING_PORCELAIN_COMMAND_LIST='add checkout filter-branch'\n \n . \"$GIT_BUILD_DIR/contrib/completion/git-completion.bash\"\n \n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348096","messageId":"CAM0VKjk4F-RfDjPLfC8_YzzJxPEM0O79uBx3kiCAMSCV4xVAMw@mail.gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-13-pclouds@gmail.com","subject":"Re: [PATCH 12/14] completion: let git provide the completable command list","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-05-20T13:20:11Z","receivedAt":"2018-05-20T13:20:16Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sat, May 19, 2018 at 6:27 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Instead of maintaining a separate list of command classification,\n> which often could go out of date, let's centralize the information\n> back in git.\n>\n> While the function in git-completion.bash implies \"list porcelain\n> commands\", that's not exactly what it does. It gets all commands (aka\n> --list-cmds=main,others) then exclude certain non-porcelain ones. We\n> could almost recreate this list two lists list-mainporcelain and\n> others. The non-porcelain-but-included-anyway is added by the third\n> category list-complete.\n>\n> list-complete does not recreate exactly the command list before this\n> patch though. The following commands will disappear from the complete\n> list because they are not in command-list.txt and it's not worth\n> adding them back: lost-found, peek-remote and tar-tree.\n\nThese commands have been removed long ago, see the topic leading to\n577aed296a (Merge branch 'jk/remove-deprecated', 2013-12-12).  Perhaps\nyou saw them only because they are still somewhere on your $PATH or\n$GIT_EXEC_PATH?\n\n> Note that the current completion script incorrectly classifies\n> filter-branch as porcelain and t9902 tests this behavior. We keep it\n> this way in t9902 because this test does not really care which\n> particular command is porcelain or plubmbing, they're just names.\n\ns/plubmbing/plumbing/\n"},{"id":"348097","messageId":"CAM0VKjnq9rm_72SLRY0WzSO=1uvGLSUV7QW0BThKMFBia-t45Q@mail.gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-14-pclouds@gmail.com","subject":"Re: [PATCH 13/14] completion: reduce completable command list","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-05-20T13:24:27Z","receivedAt":"2018-05-20T13:24:31Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sat, May 19, 2018 at 6:27 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> The following commands are removed from the complete list:\n>\n> - annotate             obsolete, discouraged to use\n> - filter-branch        not often used\n> - get-tar-commit-id    not often used\n> - imap-send            not often used\n> - interpreter-trailers not for interactive use\n> - p4                   too short and probably not often used (*)\n> - svn                  same category as p4 (*)\n> - verify-commit        not often used\n>\n\n> @@ -127,7 +127,7 @@ git-mktree                              plumbingmanipulators\n>  git-mv                                  mainporcelain           worktree\n>  git-name-rev                            plumbinginterrogators           complete\n\nSince 'git name-rev' is plumbing, and since it's functionality is\nfully covered by the 'git describe' porcelain, shouldn't it be removed\nas well?\n"},{"id":"348098","messageId":"CAM0VKjn1WyRgGZDidPjr3YatRV65h_sSzQQpg+=OUtiaTxMgpw@mail.gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-15-pclouds@gmail.com","subject":"Re: [PATCH 14/14] completion: allow to customize the completable command list","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-05-20T14:27:32Z","receivedAt":"2018-05-20T14:27:37Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sat, May 19, 2018 at 6:27 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> By default we show porcelain, external commands and a couple others\n> that are also popular. If you are not happy with this list, you can\n> now customize it. See the big comment block for details.\n>\n> PS. perhaps I should make aliases a group too, which makes it possible\n> to _not_ complete aliases by omitting this special group in\n> $GIT_COMPLETION_CMD_GROUPS\n\nNote that the completion script reads the configured aliases each time\nthe user attempts to complete commands.  So if the user adds or\nremoves an alias, then it will automatically be taken into account the\nnext time after 'git <TAB>'.  By turning aliases into a group listed\nby 'git help' they would be cached like all other commands, so this\nwould no longer be the case.\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/config.txt               | 10 ++++++++\n>  contrib/completion/git-completion.bash | 28 +++++++++++++++++++++-\n>  git.c                                  |  2 ++\n>  help.c                                 | 33 ++++++++++++++++++++++++++\n>  help.h                                 |  1 +\n>  5 files changed, 73 insertions(+), 1 deletion(-)\n>\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 2659153cb3..91f7eaed7b 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -1343,6 +1343,16 @@ credential.<url>.*::\n>  credentialCache.ignoreSIGHUP::\n>         Tell git-credential-cache--daemon to ignore SIGHUP, instead of quitting.\n>\n> +completion.commands::\n> +       This is only used by git-completion.bash to add or remove\n> +       commands from the complete list. Normally only porcelain\n\ns/complete list/list of completed commands/ perhaps?\n\n> +       commands and a few select others are in the complete list. You\n\ns/in the complete list/completed/\n\n> +       can add more commands, separated by space, in this\n> +       variable. Prefixing the command with '-' will remove it from\n> +       the existing list.\n> ++\n> +This variable should not be per-repository.\n\nI think this should also mention that changing the value of this\nconfig variable will not immediately affect the commands listed after\n'git <TAB>', but the user will have to re-dot-source the completion\nscript first.\n\nThe way I understand the rest of the patch, this config variable\ndoesn't have any effect if $GIT_COMPLETION_CMD_GROUPS doesn't contain\n\"config\".  If that is indeed the case, then that should be mentioned\nhere as well.\n\nHaving said that, I wonder whether we should really require \"config\"\nin $GIT_COMPLETION_CMD_GROUPS.  Isn't having 'completion.commands' set\nin the config a clear enough indication in itself that the user wants\nto customize the listed commands?\n\n> +\n>  include::diff-config.txt[]\n>\n>  difftool.<tool>.path::\n> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n> index cd1d8e553f..f237eb0ff4 100644\n> --- a/contrib/completion/git-completion.bash\n> +++ b/contrib/completion/git-completion.bash\n> @@ -38,6 +38,29 @@\n>  #\n>  #     When set to \"1\", do not include \"DWIM\" suggestions in git-checkout\n>  #     completion (e.g., completing \"foo\" when \"origin/foo\" exists).\n> +#\n> +#   GIT_COMPLETION_CMD_GROUPS\n> +#\n> +#     When set, \"git --list-cmds=$GIT_COMPLETION_CMD_GROUPS\" will be\n> +#     used to get the list of completable commands. The default is\n> +#     \"mainporcelain,others,list-complete\" (in English: all porcelain\n\nMental note #1: \"mainporcelain\"\n\n> +#     commands and external ones are included. Certain non-porcelain\n> +#     commands are also marked for completion in command-list.txt).\n> +#     You could for example complete all commands with\n> +#\n> +#         GIT_COMPLETION_CMD_GROUPS=main,others\n\nMental note #2: \"main\"\n\n> +#\n> +#     Or you could go with main porcelain only and extra commands in\n> +#     the configuration variable completion.commands with\n> +#\n> +#         GIT_COMPLETION_CMD_GROUPS=mainporcelain,config\n> +#\n> +#     Or go completely custom group with\n> +#\n> +#         GIT_COMPLETION_CMD_GROUPS=config\n> +#\n> +#     Or you could even play with other command categories found in\n> +#     command-list.txt.\n>\n>  case \"$COMP_WORDBREAKS\" in\n>  *:*) : great ;;\n> @@ -842,8 +865,11 @@ __git_commands () {\n>                 if test -n \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n>                 then\n>                         printf \"%s\" \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n> +               elif test -n \"$GIT_COMPLETION_CMD_GROUPS\"\n> +               then\n> +                       git --list-cmds=\"$GIT_COMPLETION_CMD_GROUPS\"\n>                 else\n> -                       git --list-cmds=list-mainporcelain,others,list-complete\n> +                       git --list-cmds=list-mainporcelain,others,list-complete,config\n\nSo first it was \"mainporcelain\", then simply \"main\", then\n\"mainporcelain\" again, and now \"list-mainporcelain\"?!\nYou've lost me here.\n\nAre the possible values documented anywhere?\n\nFurthermore, the default value mentioned in the comments above didn't\ninclude \"config\", either (but then again, I don't think we really need\n\"config\" in the first place).\n\n>                 fi\n>                 ;;\n>         all)\n> diff --git a/git.c b/git.c\n> index 4d5b8a9931..ea4feedd0b 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -60,6 +60,8 @@ static int list_cmds(const char *spec)\n>                         list_all_main_cmds(&list);\n>                 else if (match_token(spec, len, \"others\"))\n>                         list_all_other_cmds(&list);\n> +               else if (match_token(spec, len, \"config\"))\n> +                       list_cmds_by_config(&list);\n>                 else if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n>                         struct strbuf sb = STRBUF_INIT;\n>\n> diff --git a/help.c b/help.c\n> index 23924dd300..abf87205b2 100644\n> --- a/help.c\n> +++ b/help.c\n> @@ -366,6 +366,39 @@ void list_cmds_by_category(struct string_list *list,\n>         }\n>  }\n>\n> +void list_cmds_by_config(struct string_list *list)\n> +{\n> +       const char *cmd_list;\n> +\n> +       /*\n> +        * There's no actual repository setup at this point (and even\n> +        * if there is, we don't really care; only global config\n> +        * matters). If we accidentally set up a repository, it's ok\n> +        * too since the caller (git --list-cmds=) should exit shortly\n> +        * anyway.\n> +        */\n> +       if (git_config_get_string_const(\"completion.commands\", &cmd_list))\n> +               return;\n> +\n> +       string_list_sort(list);\n> +       string_list_remove_duplicates(list, 0);\n> +\n> +       while (*cmd_list) {\n> +               struct strbuf sb = STRBUF_INIT;\n> +               const char *p = strchrnul(cmd_list, ' ');\n> +\n> +               strbuf_add(&sb, cmd_list, p - cmd_list);\n> +               if (*cmd_list == '-')\n> +                       string_list_remove(list, cmd_list + 1, 0);\n> +               else\n> +                       string_list_insert(list, sb.buf);\n> +               strbuf_release(&sb);\n> +               while (*p == ' ')\n> +                       p++;\n> +               cmd_list = p;\n> +       }\n> +}\n> +\n>  void list_common_guides_help(void)\n>  {\n>         struct category_description catdesc[] = {\n> diff --git a/help.h b/help.h\n> index b2293e99be..3b38292a1b 100644\n> --- a/help.h\n> +++ b/help.h\n> @@ -26,6 +26,7 @@ extern void list_all_main_cmds(struct string_list *list);\n>  extern void list_all_other_cmds(struct string_list *list);\n>  extern void list_cmds_by_category(struct string_list *list,\n>                                   const char *category);\n> +extern void list_cmds_by_config(struct string_list *list);\n>  extern const char *help_unknown_cmd(const char *cmd);\n>  extern void load_command_list(const char *prefix,\n>                               struct cmdnames *main_cmds,\n> --\n> 2.17.0.705.g3525833791\n>\n"},{"id":"348103","messageId":"CACsJy8B29o0ZJoecrdsiVpma3iudwUCyyD_-yCe_3ePad1WKKQ@mail.gmail.com","threadId":"48522","inReplyTo":"CAM0VKjn1WyRgGZDidPjr3YatRV65h_sSzQQpg+=OUtiaTxMgpw@mail.gmail.com","subject":"Re: [PATCH 14/14] completion: allow to customize the completable command list","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T15:52:58Z","receivedAt":"2018-05-20T15:53:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, May 20, 2018 at 4:27 PM, SZEDER Gábor <szeder.dev@gmail.com> wrote:\n> On Sat, May 19, 2018 at 6:27 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> By default we show porcelain, external commands and a couple others\n>> that are also popular. If you are not happy with this list, you can\n>> now customize it. See the big comment block for details.\n>>\n>> PS. perhaps I should make aliases a group too, which makes it possible\n>> to _not_ complete aliases by omitting this special group in\n>> $GIT_COMPLETION_CMD_GROUPS\n>\n> Note that the completion script reads the configured aliases each time\n> the user attempts to complete commands.  So if the user adds or\n> removes an alias, then it will automatically be taken into account the\n> next time after 'git <TAB>'.  By turning aliases into a group listed\n> by 'git help' they would be cached like all other commands, so this\n> would no longer be the case.\n\nMaybe we can stop caching \"git --list-cmds=...\" then. We achieve the\nsame effect for completion.commands (changes take effect immediately)\nand don't add any more overhead (still one git call per completion)?\nThis can also avoid the per-repository limitation below.\n\n>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  Documentation/config.txt               | 10 ++++++++\n>>  contrib/completion/git-completion.bash | 28 +++++++++++++++++++++-\n>>  git.c                                  |  2 ++\n>>  help.c                                 | 33 ++++++++++++++++++++++++++\n>>  help.h                                 |  1 +\n>>  5 files changed, 73 insertions(+), 1 deletion(-)\n>>\n>> diff --git a/Documentation/config.txt b/Documentation/config.txt\n>> index 2659153cb3..91f7eaed7b 100644\n>> --- a/Documentation/config.txt\n>> +++ b/Documentation/config.txt\n>> @@ -1343,6 +1343,16 @@ credential.<url>.*::\n>>  credentialCache.ignoreSIGHUP::\n>>         Tell git-credential-cache--daemon to ignore SIGHUP, instead of quitting.\n>>\n>> +completion.commands::\n>> +       This is only used by git-completion.bash to add or remove\n>> +       commands from the complete list. Normally only porcelain\n>\n> s/complete list/list of completed commands/ perhaps?\n>\n>> +       commands and a few select others are in the complete list. You\n>\n> s/in the complete list/completed/\n>\n>> +       can add more commands, separated by space, in this\n>> +       variable. Prefixing the command with '-' will remove it from\n>> +       the existing list.\n>> ++\n>> +This variable should not be per-repository.\n>\n> I think this should also mention that changing the value of this\n> config variable will not immediately affect the commands listed after\n> 'git <TAB>', but the user will have to re-dot-source the completion\n> script first.\n>\n> The way I understand the rest of the patch, this config variable\n> doesn't have any effect if $GIT_COMPLETION_CMD_GROUPS doesn't contain\n> \"config\".  If that is indeed the case, then that should be mentioned\n> here as well.\n>\n> Having said that, I wonder whether we should really require \"config\"\n> in $GIT_COMPLETION_CMD_GROUPS.  Isn't having 'completion.commands' set\n> in the config a clear enough indication in itself that the user wants\n> to customize the listed commands?\n\n$GIT_COMPLETION_CMD_GROUPS is for really specific customization. I\ninitially added it because \"config\" was not default, and because\ncompletion.commands could not exclude commands, only include them. Now\nthat is possible, perhaps just completion.commands (no\n$GIT_COMPLETINO_CMD_GROUPS) would suffice for most cases?\n\n>\n>> +\n>>  include::diff-config.txt[]\n>>\n>>  difftool.<tool>.path::\n>> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n>> index cd1d8e553f..f237eb0ff4 100644\n>> --- a/contrib/completion/git-completion.bash\n>> +++ b/contrib/completion/git-completion.bash\n>> @@ -38,6 +38,29 @@\n>>  #\n>>  #     When set to \"1\", do not include \"DWIM\" suggestions in git-checkout\n>>  #     completion (e.g., completing \"foo\" when \"origin/foo\" exists).\n>> +#\n>> +#   GIT_COMPLETION_CMD_GROUPS\n>> +#\n>> +#     When set, \"git --list-cmds=$GIT_COMPLETION_CMD_GROUPS\" will be\n>> +#     used to get the list of completable commands. The default is\n>> +#     \"mainporcelain,others,list-complete\" (in English: all porcelain\n>\n> Mental note #1: \"mainporcelain\"\n>\n>> +#     commands and external ones are included. Certain non-porcelain\n>> +#     commands are also marked for completion in command-list.txt).\n>> +#     You could for example complete all commands with\n>> +#\n>> +#         GIT_COMPLETION_CMD_GROUPS=main,others\n>\n> Mental note #2: \"main\"\n>\n>> +#\n>> +#     Or you could go with main porcelain only and extra commands in\n>> +#     the configuration variable completion.commands with\n>> +#\n>> +#         GIT_COMPLETION_CMD_GROUPS=mainporcelain,config\n>> +#\n>> +#     Or go completely custom group with\n>> +#\n>> +#         GIT_COMPLETION_CMD_GROUPS=config\n>> +#\n>> +#     Or you could even play with other command categories found in\n>> +#     command-list.txt.\n>>\n>>  case \"$COMP_WORDBREAKS\" in\n>>  *:*) : great ;;\n>> @@ -842,8 +865,11 @@ __git_commands () {\n>>                 if test -n \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n>>                 then\n>>                         printf \"%s\" \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n>> +               elif test -n \"$GIT_COMPLETION_CMD_GROUPS\"\n>> +               then\n>> +                       git --list-cmds=\"$GIT_COMPLETION_CMD_GROUPS\"\n>>                 else\n>> -                       git --list-cmds=list-mainporcelain,others,list-complete\n>> +                       git --list-cmds=list-mainporcelain,others,list-complete,config\n>\n> So first it was \"mainporcelain\", then simply \"main\", then\n> \"mainporcelain\" again, and now \"list-mainporcelain\"?!\n> You've lost me here.\n>\n> Are the possible values documented anywhere?\n\nIn the code :) I did not document it because it was hidden option and\nit may change again. list-XXX corresponds to the XXX tag in\ncommand-list.txt while the no \"list-\" like \"main\", \"config\", or\n\"others\" provide the command list from other sources (e.g. libexec for\n\"main\", $PATH for \"ohers\", completion.commands for \"config\")\n\n> Furthermore, the default value mentioned in the comments above didn't\n> include \"config\", either (but then again, I don't think we really need\n> \"config\" in the first place).\n\nYeah that comment block was out of date. I think including \"config\" by\ndefault gives a smoother experience. You just update\ncompletion.commands and ready, you don't need to export\n$GITC_COMPLETION_CMD_GROUPS before sourcing git-completion.bash\n(which, depends on distro, may be done automatically and hard to\nintervene except modifying the script itself).\n-- \nDuy\n"},{"id":"348104","messageId":"CACsJy8DcDku-8M2+Q6m2BGFxsR+-qDHqX2w4-LXBawsQ3nqXLA@mail.gmail.com","threadId":"48522","inReplyTo":"CAM0VKjk4F-RfDjPLfC8_YzzJxPEM0O79uBx3kiCAMSCV4xVAMw@mail.gmail.com","subject":"Re: [PATCH 12/14] completion: let git provide the completable command list","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T15:57:47Z","receivedAt":"2018-05-20T15:58:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, May 20, 2018 at 3:20 PM, SZEDER Gábor <szeder.dev@gmail.com> wrote:\n> On Sat, May 19, 2018 at 6:27 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> Instead of maintaining a separate list of command classification,\n>> which often could go out of date, let's centralize the information\n>> back in git.\n>>\n>> While the function in git-completion.bash implies \"list porcelain\n>> commands\", that's not exactly what it does. It gets all commands (aka\n>> --list-cmds=main,others) then exclude certain non-porcelain ones. We\n>> could almost recreate this list two lists list-mainporcelain and\n>> others. The non-porcelain-but-included-anyway is added by the third\n>> category list-complete.\n>>\n>> list-complete does not recreate exactly the command list before this\n>> patch though. The following commands will disappear from the complete\n>> list because they are not in command-list.txt and it's not worth\n>> adding them back: lost-found, peek-remote and tar-tree.\n>\n> These commands have been removed long ago, see the topic leading to\n> 577aed296a (Merge branch 'jk/remove-deprecated', 2013-12-12).  Perhaps\n> you saw them only because they are still somewhere on your $PATH or\n> $GIT_EXEC_PATH?\n\nRight. Old commands remain in my libexec dir. Will clean up this commit message.\n-- \nDuy\n"},{"id":"348109","messageId":"20180520184009.976-1-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180519042752.8666-1-pclouds@gmail.com","subject":"[PATCH v2 00/17] nd/command-list updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:39:52Z","receivedAt":"2018-05-20T18:40:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It turns out this series is not done as I thought it was :)\n\nv2 makes it possible to write\n\n    gitcomp \"$(git --list-cmds=...)\"\n\nwhich is really nice and very close to what gitcomp_builtin does.\nOther changes are\n\n- support --list-cmds=alias and --list-cmds=nohelpers so that we could\n  do the above\n- a bit more document about --list-cmds\n- $GIT_COMPLETION_CMD_GROUPS is dropped. I think it just causes more\n  confusion and complete.commands should be enough in most cases\n- name-rev is no longer compleable\n- \"git help <tab>\" does not show truly external commands anymore\n  (those that are in $PATH but not in libexec and very unlikely to\n  have a man page)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 91f7eaed7b..9e81dcf867 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1345,13 +1345,11 @@ credentialCache.ignoreSIGHUP::\n \n completion.commands::\n \tThis is only used by git-completion.bash to add or remove\n-\tcommands from the complete list. Normally only porcelain\n-\tcommands and a few select others are in the complete list. You\n+\tcommands from the list of completed commands. Normally only\n+\tporcelain commands and a few select others are completed. You\n \tcan add more commands, separated by space, in this\n \tvariable. Prefixing the command with '-' will remove it from\n \tthe existing list.\n-+\n-This variable should not be per-repository.\n \n include::diff-config.txt[]\n \ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 4767860e72..6f7eddf847 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -163,6 +163,16 @@ foo.bar= ...`) sets `foo.bar` to the empty string which `git config\n \tDo not perform optional operations that require locks. This is\n \tequivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.\n \n+--list-cmds=group[,group...]::\n+\tList commands by group. This is an internal/experimental\n+\toption and may change or be removed in the future. Supported\n+\tgroups are: builtins, parseopt (builtin commands that use\n+\tparse-options), main (all commands in libexec directory),\n+\tothers (all other commands in `$PATH` that have git- prefix),\n+\tlist-<category> (see categories in command-list.txt),\n+\tnohelpers (exclude helper commands), alias and config\n+\t(retrieve command list from config variable completion.commands)\n+\n GIT COMMANDS\n ------------\n \ndiff --git a/alias.c b/alias.c\nindex bf146e5263..a7e4e57130 100644\n--- a/alias.c\n+++ b/alias.c\n@@ -1,9 +1,12 @@\n #include \"cache.h\"\n+#include \"alias.h\"\n #include \"config.h\"\n+#include \"string-list.h\"\n \n struct config_alias_data {\n \tconst char *alias;\n \tchar *v;\n+\tstruct string_list *list;\n };\n \n static int config_alias_cb(const char *key, const char *value, void *d)\n@@ -11,8 +14,16 @@ static int config_alias_cb(const char *key, const char *value, void *d)\n \tstruct config_alias_data *data = d;\n \tconst char *p;\n \n-\tif (skip_prefix(key, \"alias.\", &p) && !strcasecmp(p, data->alias))\n-\t\treturn git_config_string((const char **)&data->v, key, value);\n+\tif (!skip_prefix(key, \"alias.\", &p))\n+\t\treturn 0;\n+\n+\tif (data->alias) {\n+\t\tif (!strcasecmp(p, data->alias))\n+\t\t\treturn git_config_string((const char **)&data->v,\n+\t\t\t\t\t\t key, value);\n+\t} else if (data->list) {\n+\t\tstring_list_append(data->list, p);\n+\t}\n \n \treturn 0;\n }\n@@ -26,6 +37,13 @@ char *alias_lookup(const char *alias)\n \treturn data.v;\n }\n \n+void list_aliases(struct string_list *list)\n+{\n+\tstruct config_alias_data data = { NULL, NULL, list };\n+\n+\tread_early_config(config_alias_cb, &data);\n+}\n+\n #define SPLIT_CMDLINE_BAD_ENDING 1\n #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2\n static const char *split_cmdline_errors[] = {\ndiff --git a/alias.h b/alias.h\nnew file mode 100644\nindex 0000000000..79933f2457\n--- /dev/null\n+++ b/alias.h\n@@ -0,0 +1,12 @@\n+#ifndef __ALIAS_H__\n+#define __ALIAS_H__\n+\n+struct string_list;\n+\n+char *alias_lookup(const char *alias);\n+int split_cmdline(char *cmdline, const char ***argv);\n+/* Takes a negative value returned by split_cmdline */\n+const char *split_cmdline_strerror(int cmdline_errno);\n+void list_aliases(struct string_list *list);\n+\n+#endif\ndiff --git a/builtin/help.c b/builtin/help.c\nindex 5727fb5e51..6b4b3df90d 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -9,6 +9,7 @@\n #include \"run-command.h\"\n #include \"column.h\"\n #include \"help.h\"\n+#include \"alias.h\"\n \n #ifndef DEFAULT_HELP_FORMAT\n #define DEFAULT_HELP_FORMAT \"man\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 9db5a2cf16..e3681cd850 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -34,6 +34,7 @@\n #include \"string-list.h\"\n #include \"packfile.h\"\n #include \"tag.h\"\n+#include \"alias.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\ndiff --git a/cache.h b/cache.h\nindex bbaf5c349a..111116ea13 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1835,11 +1835,6 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule);\n void overlay_tree_on_index(struct index_state *istate,\n \t\t\t   const char *tree_name, const char *prefix);\n \n-char *alias_lookup(const char *alias);\n-int split_cmdline(char *cmdline, const char ***argv);\n-/* Takes a negative value returned by split_cmdline */\n-const char *split_cmdline_strerror(int cmdline_errno);\n-\n /* setup.c */\n struct startup_info {\n \tint have_repository;\ndiff --git a/command-list.txt b/command-list.txt\nindex 8462ea475f..e505a1e34c 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -125,7 +125,7 @@ git-merge-tree                          ancillaryinterrogators\n git-mktag                               plumbingmanipulators\n git-mktree                              plumbingmanipulators\n git-mv                                  mainporcelain           worktree\n-git-name-rev                            plumbinginterrogators           complete\n+git-name-rev                            plumbinginterrogators\n git-notes                               mainporcelain\n git-p4                                  foreignscminterface\n git-pack-objects                        plumbingmanipulators\ndiff --git a/connect.c b/connect.c\nindex c3a014c5ba..ff078d28dc 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -13,6 +13,7 @@\n #include \"transport.h\"\n #include \"strbuf.h\"\n #include \"protocol.h\"\n+#include \"alias.h\"\n \n static char *server_capabilities;\n static const char *parse_feature_value(const char *, const char *, int *);\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex f237eb0ff4..e5b2ccbdd2 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -38,29 +38,6 @@\n #\n #     When set to \"1\", do not include \"DWIM\" suggestions in git-checkout\n #     completion (e.g., completing \"foo\" when \"origin/foo\" exists).\n-#\n-#   GIT_COMPLETION_CMD_GROUPS\n-#\n-#     When set, \"git --list-cmds=$GIT_COMPLETION_CMD_GROUPS\" will be\n-#     used to get the list of completable commands. The default is\n-#     \"mainporcelain,others,list-complete\" (in English: all porcelain\n-#     commands and external ones are included. Certain non-porcelain\n-#     commands are also marked for completion in command-list.txt).\n-#     You could for example complete all commands with\n-#\n-#         GIT_COMPLETION_CMD_GROUPS=main,others\n-#\n-#     Or you could go with main porcelain only and extra commands in\n-#     the configuration variable completion.commands with\n-#\n-#         GIT_COMPLETION_CMD_GROUPS=mainporcelain,config\n-#\n-#     Or go completely custom group with\n-#\n-#         GIT_COMPLETION_CMD_GROUPS=config\n-#\n-#     Or you could even play with other command categories found in\n-#     command-list.txt.\n \n case \"$COMP_WORDBREAKS\" in\n *:*) : great ;;\n@@ -857,66 +834,11 @@ __git_complete_strategy ()\n \treturn 1\n }\n \n-# __git_commands requires 1 argument:\n-# 1: the command group, either \"all\" or \"porcelain\"\n-__git_commands () {\n-\tcase \"$1\" in\n-\tporcelain)\n-\t\tif test -n \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n-\t\tthen\n-\t\t\tprintf \"%s\" \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n-\t\telif test -n \"$GIT_COMPLETION_CMD_GROUPS\"\n-\t\tthen\n-\t\t\tgit --list-cmds=\"$GIT_COMPLETION_CMD_GROUPS\"\n-\t\telse\n-\t\t\tgit --list-cmds=list-mainporcelain,others,list-complete,config\n-\t\tfi\n-\t\t;;\n-\tall)\n-\t\tif test -n \"$GIT_TESTING_ALL_COMMAND_LIST\"\n-\t\tthen\n-\t\t\tprintf \"%s\" \"$GIT_TESTING_ALL_COMMAND_LIST\"\n-\t\telse\n-\t\t\tgit --list-cmds=main,others\n-\t\tfi\n-\t\t;;\n-\tesac\n-}\n-\n-__git_list_commands ()\n-{\n-\tlocal i IFS=\" \"$'\\n'\n-\tfor i in $(__git_commands $1)\n-\tdo\n-\t\tcase $i in\n-\t\t*--*)             : helper pattern;;\n-\t\t*) echo $i;;\n-\t\tesac\n-\tdone\n-}\n-\n-__git_list_all_commands ()\n-{\n-\t__git_list_commands all\n-}\n-\n __git_all_commands=\n __git_compute_all_commands ()\n {\n \ttest -n \"$__git_all_commands\" ||\n-\t__git_all_commands=$(__git_list_all_commands)\n-}\n-\n-__git_list_porcelain_commands ()\n-{\n-\t__git_list_commands porcelain\n-}\n-\n-__git_porcelain_commands=\n-__git_compute_porcelain_commands ()\n-{\n-\ttest -n \"$__git_porcelain_commands\" ||\n-\t__git_porcelain_commands=$(__git_list_porcelain_commands)\n+\t__git_all_commands=$(git --list-cmds=main,others,alias,nohelpers)\n }\n \n # Lists all set config variables starting with the given section prefix,\n@@ -934,11 +856,6 @@ __git_pretty_aliases ()\n \t__git_get_config_variables \"pretty\"\n }\n \n-__git_aliases ()\n-{\n-\t__git_get_config_variables \"alias\"\n-}\n-\n # __git_aliased_command requires 1 argument\n __git_aliased_command ()\n {\n@@ -1538,13 +1455,6 @@ _git_grep ()\n \t__git_complete_refs\n }\n \n-__git_all_guides=\n-__git_compute_all_guides ()\n-{\n-\ttest -n \"$__git_all_guides\" ||\n-\t__git_all_guides=$(git --list-cmds=list-guide)\n-}\n-\n _git_help ()\n {\n \tcase \"$cur\" in\n@@ -1553,11 +1463,12 @@ _git_help ()\n \t\treturn\n \t\t;;\n \tesac\n-\t__git_compute_all_commands\n-\t__git_compute_all_guides\n-\t__gitcomp \"$__git_all_commands $(__git_aliases) $__git_all_guides\n-\t\tgitk\n-\t\t\"\n+\tif test -n \"$GIT_TESTING_ALL_COMMAND_LIST\"\n+\tthen\n+\t\t__gitcomp \"$GIT_TESTING_ALL_COMMAND_LIST $(git --list-cmds=alias,list-guide) gitk\"\n+\telse\n+\t\t__gitcomp \"$(git --list-cmds=main,nohelpers,alias,list-guide) gitk\"\n+\tfi\n }\n \n _git_init ()\n@@ -3096,8 +3007,14 @@ __git_main ()\n \t\t\t--help\n \t\t\t\"\n \t\t\t;;\n-\t\t*)     __git_compute_porcelain_commands\n-\t\t       __gitcomp \"$__git_porcelain_commands $(__git_aliases)\" ;;\n+\t\t*)\n+\t\t\tif test -n \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n+\t\t\tthen\n+\t\t\t\t__gitcomp \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n+\t\t\telse\n+\t\t\t\t__gitcomp \"$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)\"\n+\t\t\tfi\n+\t\t\t;;\n \t\tesac\n \t\treturn\n \tfi\ndiff --git a/git.c b/git.c\nindex ea4feedd0b..447dac0e71 100644\n--- a/git.c\n+++ b/git.c\n@@ -3,6 +3,7 @@\n #include \"exec_cmd.h\"\n #include \"help.h\"\n #include \"run-command.h\"\n+#include \"alias.h\"\n \n #define RUN_SETUP\t\t(1<<0)\n #define RUN_SETUP_GENTLY\t(1<<1)\n@@ -38,6 +39,18 @@ static int use_pager = -1;\n \n static void list_builtins(struct string_list *list, unsigned int exclude_option);\n \n+static void exclude_helpers_from_list(struct string_list *list)\n+{\n+\tint i = 0;\n+\n+\twhile (i < list->nr) {\n+\t\tif (strstr(list->items[i].string, \"--\"))\n+\t\t\tunsorted_string_list_delete_item(list, i, 0);\n+\t\telse\n+\t\t\ti++;\n+\t}\n+}\n+\n static int match_token(const char *spec, int len, const char *token)\n {\n \tint token_len = strlen(token);\n@@ -60,6 +73,10 @@ static int list_cmds(const char *spec)\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n \t\t\tlist_all_other_cmds(&list);\n+\t\telse if (match_token(spec, len, \"nohelpers\"))\n+\t\t\texclude_helpers_from_list(&list);\n+\t\telse if (match_token(spec, len, \"alias\"))\n+\t\t\tlist_aliases(&list);\n \t\telse if (match_token(spec, len, \"config\"))\n \t\t\tlist_cmds_by_config(&list);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\ndiff --git a/pager.c b/pager.c\nindex 92b23e6cd1..1f4688fa03 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"sigchain.h\"\n+#include \"alias.h\"\n \n #ifndef DEFAULT_PAGER\n #define DEFAULT_PAGER \"less\"\ndiff --git a/sequencer.c b/sequencer.c\nindex 667f35ebdf..1288a36ebd 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -23,6 +23,7 @@\n #include \"hashmap.h\"\n #include \"notes-utils.h\"\n #include \"sigchain.h\"\n+#include \"alias.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \ndiff --git a/shell.c b/shell.c\nindex 234b2d4f16..3ce77b8e34 100644\n--- a/shell.c\n+++ b/shell.c\n@@ -3,6 +3,7 @@\n #include \"exec_cmd.h\"\n #include \"strbuf.h\"\n #include \"run-command.h\"\n+#include \"alias.h\"\n \n #define COMMAND_DIR \"git-shell-commands\"\n #define HELP_COMMAND COMMAND_DIR \"/help\"\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 2f16679380..5863b1acac 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -1192,17 +1192,6 @@ test_expect_success '__git_pretty_aliases' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success '__git_aliases' '\n-\tcat >expect <<-EOF &&\n-\tci\n-\tco\n-\tEOF\n-\ttest_config alias.ci commit &&\n-\ttest_config alias.co checkout &&\n-\t__git_aliases >actual &&\n-\ttest_cmp expect actual\n-'\n-\n test_expect_success 'basic' '\n \trun_completion \"git \" &&\n \t# built-in\n@@ -1511,13 +1500,6 @@ test_expect_success 'sourcing the completion script clears cached commands' '\n \tverbose test -z \"$__git_all_commands\"\n '\n \n-test_expect_success 'sourcing the completion script clears cached porcelain commands' '\n-\t__git_compute_porcelain_commands &&\n-\tverbose test -n \"$__git_porcelain_commands\" &&\n-\t. \"$GIT_BUILD_DIR/contrib/completion/git-completion.bash\" &&\n-\tverbose test -z \"$__git_porcelain_commands\"\n-'\n-\n test_expect_success !GETTEXT_POISON 'sourcing the completion script clears cached merge strategies' '\n \t__git_compute_merge_strategies &&\n \tverbose test -n \"$__git_merge_strategies\" &&\n\n\nNguyễn Thái Ngọc Duy (17):\n  generate-cmds.sh: factor out synopsis extract code\n  generate-cmds.sh: export all commands to command-list.h\n  help: use command-list.h for common command list\n  Remove common-cmds.h\n  git.c: convert --list-* to --list-cmds=*\n  git --list-cmds: collect command list in a string_list\n  completion: implement and use --list-cmds=main,others\n  git: support --list-cmds=list-<category>\n  help: add \"-a --verbose\" to list all commands with synopsis\n  help: use command-list.txt for the source of guides\n  command-list.txt: documentation and guide line\n  completion: let git provide the completable command list\n  completion: reduce completable command list\n  Move declaration for alias.c to alias.h\n  completion: add and use --list-cmds=nohelpers\n  completion: add and use --list-cmds=alias\n  completion: allow to customize the completable command list\n\n .gitignore                             |   2 +-\n Documentation/config.txt               |   8 +\n Documentation/git-help.txt             |   4 +-\n Documentation/git.txt                  |  10 +\n Documentation/gitattributes.txt        |   2 +-\n Documentation/gitmodules.txt           |   2 +-\n Documentation/gitrevisions.txt         |   2 +-\n Makefile                               |  16 +-\n alias.c                                |  22 ++-\n alias.h                                |  12 ++\n builtin/help.c                         |  40 +---\n builtin/merge.c                        |   1 +\n cache.h                                |   5 -\n command-list.txt                       | 110 ++++++++---\n connect.c                              |   1 +\n contrib/completion/git-completion.bash | 147 ++-------------\n generate-cmdlist.sh                    | 126 ++++++++-----\n git.c                                  |  85 ++++++++-\n help.c                                 | 244 ++++++++++++++++++++++---\n help.h                                 |  10 +\n pager.c                                |   1 +\n sequencer.c                            |   1 +\n shell.c                                |   1 +\n t/t0012-help.sh                        |  26 ++-\n t/t9902-completion.sh                  |  23 +--\n 25 files changed, 589 insertions(+), 312 deletions(-)\n create mode 100644 alias.h\n\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348110","messageId":"20180520184009.976-2-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 01/17] generate-cmds.sh: factor out synopsis extract code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:39:53Z","receivedAt":"2018-05-20T18:40:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes it easier to reuse the same code in another place (very\nsoon).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n generate-cmdlist.sh | 18 +++++++++++-------\n 1 file changed, 11 insertions(+), 7 deletions(-)\n\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex eeea4b67ea..31b6d886cb 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -1,5 +1,15 @@\n #!/bin/sh\n \n+get_synopsis () {\n+\tsed -n '\n+\t\t/^NAME/,/'\"$1\"'/H\n+\t\t${\n+\t\t\tx\n+\t\t\ts/.*'\"$1\"' - \\(.*\\)/N_(\"\\1\")/\n+\t\t\tp\n+\t\t}' \"Documentation/$1.txt\"\n+}\n+\n echo \"/* Automatically generated by generate-cmdlist.sh */\n struct cmdname_help {\n \tchar name[16];\n@@ -39,12 +49,6 @@ sort |\n while read cmd tags\n do\n \ttag=$(echo \"$tags\" | sed \"$substnum; s/[^0-9]//g\")\n-\tsed -n '\n-\t\t/^NAME/,/git-'\"$cmd\"'/H\n-\t\t${\n-\t\t\tx\n-\t\t\ts/.*git-'\"$cmd\"' - \\(.*\\)/\t{\"'\"$cmd\"'\", N_(\"\\1\"), '$tag'},/\n-\t\t\tp\n-\t\t}' \"Documentation/git-$cmd.txt\"\n+\techo \"\t{\\\"$cmd\\\", $(get_synopsis git-$cmd), $tag},\"\n done\n echo \"};\"\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348111","messageId":"20180520184009.976-3-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 02/17] generate-cmds.sh: export all commands to command-list.h","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:39:54Z","receivedAt":"2018-05-20T18:40:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The current generate-cmds.sh generates just enough to print \"git help\"\noutput. That is, it only extracts help text for common commands.\n\nThe script is now updated to extract help text for all commands and\nkeep command classification a new file, command-list.h. This will be\nuseful later:\n\n- \"git help -a\" could print a short summary of all commands instead of\n  just the common ones.\n\n- \"git\" could produce a list of commands of one or more category. One\n  of its use is to reduce another command classification embedded in\n  git-completion.bash.\n\nThe new file can be generated but is not used anywhere yet. The plan\nis we migrate away from common-cmds.h. Then we can kill off\ncommon-cmds.h build rules and generation code (and also delete\nduplicate content in command-list.h which we keep for now to not mess\ngenerate-cmds.sh up too much).\n\nPS. The new fixed column requirement on command-list.txt is\ntechnically not needed. But it helps simplify the code a bit at this\nstage. We could lift this restriction later if we want to.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore          |  1 +\n Makefile            | 13 ++++++---\n command-list.txt    |  4 +--\n generate-cmdlist.sh | 67 ++++++++++++++++++++++++++++++++++++++++++---\n 4 files changed, 75 insertions(+), 10 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 833ef3b0b7..d4c3914167 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -180,6 +180,7 @@\n /gitweb/static/gitweb.js\n /gitweb/static/gitweb.min.*\n /common-cmds.h\n+/command-list.h\n *.tar.gz\n *.dsc\n *.deb\ndiff --git a/Makefile b/Makefile\nindex f181687250..2a8913ea21 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -757,7 +757,7 @@ LIB_FILE = libgit.a\n XDIFF_LIB = xdiff/lib.a\n VCSSVN_LIB = vcs-svn/lib.a\n \n-GENERATED_H += common-cmds.h\n+GENERATED_H += common-cmds.h command-list.h\n \n LIB_H = $(shell $(FIND) . \\\n \t-name .git -prune -o \\\n@@ -1938,6 +1938,11 @@ $(BUILT_INS): git$X\n common-cmds.h: generate-cmdlist.sh command-list.txt\n \n common-cmds.h: $(wildcard Documentation/git-*.txt)\n+\t$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt COMMON >$@+ && mv $@+ $@\n+\n+command-list.h: generate-cmdlist.sh command-list.txt\n+\n+command-list.h: $(wildcard Documentation/git-*.txt)\n \t$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@\n \n SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\\\n@@ -2148,7 +2153,7 @@ else\n # Dependencies on header files, for platforms that do not support\n # the gcc -MMD option.\n #\n-# Dependencies on automatically generated headers such as common-cmds.h\n+# Dependencies on automatically generated headers such as common-cmds.h or command-list.h\n # should _not_ be included here, since they are necessary even when\n # building an object for the first time.\n \n@@ -2527,7 +2532,7 @@ sparse: $(SP_OBJ)\n style:\n \tgit clang-format --style file --diff --extensions c,h\n \n-check: common-cmds.h\n+check: common-cmds.h command-list.h\n \t@if sparse; \\\n \tthen \\\n \t\techo >&2 \"Use 'make sparse' instead\"; \\\n@@ -2775,7 +2780,7 @@ clean: profile-clean coverage-clean\n \t$(RM) $(TEST_PROGRAMS) $(NO_INSTALL)\n \t$(RM) -r bin-wrappers $(dep_dirs)\n \t$(RM) -r po/build/\n-\t$(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*\n+\t$(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h command-list.h $(ETAGS_TARGET) tags cscope*\n \t$(RM) -r $(GIT_TARNAME) .doc-tmp-dir\n \t$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\ndiff --git a/command-list.txt b/command-list.txt\nindex a1fad28fd8..786536aba0 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -8,8 +8,8 @@ info         examine the history and state (see also: git help revisions)\n history      grow, mark and tweak your common history\n remote       collaborate (see also: git help workflows)\n \n-### command list (do not change this line)\n-# command name                          category [deprecated] [common]\n+### command list (do not change this line, also do not change alignment)\n+# command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\n git-am                                  mainporcelain\n git-annotate                            ancillaryinterrogators\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex 31b6d886cb..870d3b626a 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -1,5 +1,27 @@\n #!/bin/sh\n \n+die () {\n+\techo \"$@\" >&2\n+\texit 1\n+}\n+\n+command_list () {\n+\tsed '1,/^### command list/d;/^#/d' \"$1\"\n+}\n+\n+get_categories () {\n+\ttr ' ' '\\n'|\n+\tgrep -v '^$' |\n+\tsort |\n+\tuniq\n+}\n+\n+category_list () {\n+\tcommand_list \"$1\" |\n+\tcut -c 40- |\n+\tget_categories\n+}\n+\n get_synopsis () {\n \tsed -n '\n \t\t/^NAME/,/'\"$1\"'/H\n@@ -10,14 +32,51 @@ get_synopsis () {\n \t\t}' \"Documentation/$1.txt\"\n }\n \n+define_categories () {\n+\techo\n+\techo \"/* Command categories */\"\n+\tbit=0\n+\tcategory_list \"$1\" |\n+\twhile read cat\n+\tdo\n+\t\techo \"#define CAT_$cat (1UL << $bit)\"\n+\t\tbit=$(($bit+1))\n+\tdone\n+\ttest \"$bit\" -gt 32 && die \"Urgh.. too many categories?\"\n+}\n+\n+print_command_list () {\n+\techo \"static struct cmdname_help command_list[] = {\"\n+\n+\tcommand_list \"$1\" |\n+\twhile read cmd rest\n+\tdo\n+\t\tprintf \"\t{ \\\"$cmd\\\", $(get_synopsis $cmd), 0\"\n+\t\tfor cat in $(echo \"$rest\" | get_categories)\n+\t\tdo\n+\t\t\tprintf \" | CAT_$cat\"\n+\t\tdone\n+\t\techo \" },\"\n+\tdone\n+\techo \"};\"\n+}\n+\n echo \"/* Automatically generated by generate-cmdlist.sh */\n struct cmdname_help {\n-\tchar name[16];\n-\tchar help[80];\n-\tunsigned char group;\n+\tconst char *name;\n+\tconst char *help;\n+\tuint32_t group;\n };\n+\"\n+if test -z \"$2\"\n+then\n+\tdefine_categories \"$1\"\n+\techo\n+\tprint_command_list \"$1\"\n+\texit 0\n+fi\n \n-static const char *common_cmd_groups[] = {\"\n+echo \"static const char *common_cmd_groups[] = {\"\n \n grps=grps$$.tmp\n match=match$$.tmp\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348112","messageId":"20180520184009.976-4-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 03/17] help: use command-list.h for common command list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:39:55Z","receivedAt":"2018-05-20T18:40:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The previous commit added code generation for all_cmd_desc[] which\nincludes almost everything we need to generate common command list.\nConvert help code to use that array instead and drop common_cmds[] array.\n\nThe description of each common command group is removed from\ncommand-list.txt. This keeps this file format simpler. common-cmds.h\nwill not be generated correctly after this change due to the\ncommand-list.txt format change. But it does not matter and\ncommon-cmds.h will be removed.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile            |   4 +-\n command-list.txt    |  10 ---\n generate-cmdlist.sh |   4 +-\n help.c              | 145 +++++++++++++++++++++++++++++++++-----------\n t/t0012-help.sh     |   9 +++\n 5 files changed, 122 insertions(+), 50 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 2a8913ea21..5c58b0b692 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1914,9 +1914,9 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n \t\t$(filter %.o,$^) $(LIBS)\n \n-help.sp help.s help.o: common-cmds.h\n+help.sp help.s help.o: common-cmds.h command-list.h\n \n-builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h GIT-PREFIX\n+builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h command-list.h GIT-PREFIX\n builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \\\n \t'-DGIT_HTML_PATH=\"$(htmldir_relative_SQ)\"' \\\n \t'-DGIT_MAN_PATH=\"$(mandir_relative_SQ)\"' \\\ndiff --git a/command-list.txt b/command-list.txt\nindex 786536aba0..3bd23201a6 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -1,13 +1,3 @@\n-# common commands are grouped by themes\n-# these groups are output by 'git help' in the order declared here.\n-# map each common command in the command list to one of these groups.\n-### common groups (do not change this line)\n-init         start a working area (see also: git help tutorial)\n-worktree     work on the current change (see also: git help everyday)\n-info         examine the history and state (see also: git help revisions)\n-history      grow, mark and tweak your common history\n-remote       collaborate (see also: git help workflows)\n-\n ### command list (do not change this line, also do not change alignment)\n # command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex 870d3b626a..9eb22c4ef1 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -6,7 +6,7 @@ die () {\n }\n \n command_list () {\n-\tsed '1,/^### command list/d;/^#/d' \"$1\"\n+\tgrep -v '^#' \"$1\"\n }\n \n get_categories () {\n@@ -65,7 +65,7 @@ echo \"/* Automatically generated by generate-cmdlist.sh */\n struct cmdname_help {\n \tconst char *name;\n \tconst char *help;\n-\tuint32_t group;\n+\tuint32_t category;\n };\n \"\n if test -z \"$2\"\ndiff --git a/help.c b/help.c\nindex 60071a9bea..2d6a3157f8 100644\n--- a/help.c\n+++ b/help.c\n@@ -5,13 +5,114 @@\n #include \"run-command.h\"\n #include \"levenshtein.h\"\n #include \"help.h\"\n-#include \"common-cmds.h\"\n+#include \"command-list.h\"\n #include \"string-list.h\"\n #include \"column.h\"\n #include \"version.h\"\n #include \"refs.h\"\n #include \"parse-options.h\"\n \n+struct category_description {\n+\tuint32_t category;\n+\tconst char *desc;\n+};\n+static uint32_t common_mask =\n+\tCAT_init | CAT_worktree | CAT_info |\n+\tCAT_history | CAT_remote;\n+static struct category_description common_categories[] = {\n+\t{ CAT_init, N_(\"start a working area (see also: git help tutorial)\") },\n+\t{ CAT_worktree, N_(\"work on the current change (see also: git help everyday)\") },\n+\t{ CAT_info, N_(\"examine the history and state (see also: git help revisions)\") },\n+\t{ CAT_history, N_(\"grow, mark and tweak your common history\") },\n+\t{ CAT_remote, N_(\"collaborate (see also: git help workflows)\") },\n+\t{ 0, NULL }\n+};\n+\n+static const char *drop_prefix(const char *name)\n+{\n+\tconst char *new_name;\n+\n+\tif (skip_prefix(name, \"git-\", &new_name))\n+\t\treturn new_name;\n+\treturn name;\n+\n+}\n+\n+static void extract_cmds(struct cmdname_help **p_cmds, uint32_t mask)\n+{\n+\tint i, nr = 0;\n+\tstruct cmdname_help *cmds;\n+\n+\tif (ARRAY_SIZE(command_list) == 0)\n+\t\tBUG(\"empty command_list[] is a sign of broken generate-cmdlist.sh\");\n+\n+\tALLOC_ARRAY(cmds, ARRAY_SIZE(command_list) + 1);\n+\n+\tfor (i = 0; i < ARRAY_SIZE(command_list); i++) {\n+\t\tconst struct cmdname_help *cmd = command_list + i;\n+\n+\t\tif (!(cmd->category & mask))\n+\t\t\tcontinue;\n+\n+\t\tcmds[nr] = *cmd;\n+\t\tcmds[nr].name = drop_prefix(cmd->name);\n+\n+\t\tnr++;\n+\t}\n+\tcmds[nr].name = NULL;\n+\t*p_cmds = cmds;\n+}\n+\n+static void print_command_list(const struct cmdname_help *cmds,\n+\t\t\t       uint32_t mask, int longest)\n+{\n+\tint i;\n+\n+\tfor (i = 0; cmds[i].name; i++) {\n+\t\tif (cmds[i].category & mask) {\n+\t\t\tprintf(\"   %s   \", cmds[i].name);\n+\t\t\tmput_char(' ', longest - strlen(cmds[i].name));\n+\t\t\tputs(_(cmds[i].help));\n+\t\t}\n+\t}\n+}\n+\n+static int cmd_name_cmp(const void *elem1, const void *elem2)\n+{\n+\tconst struct cmdname_help *e1 = elem1;\n+\tconst struct cmdname_help *e2 = elem2;\n+\n+\treturn strcmp(e1->name, e2->name);\n+}\n+\n+static void print_cmd_by_category(const struct category_description *catdesc)\n+{\n+\tstruct cmdname_help *cmds;\n+\tint longest = 0;\n+\tint i, nr = 0;\n+\tuint32_t mask = 0;\n+\n+\tfor (i = 0; catdesc[i].desc; i++)\n+\t\tmask |= catdesc[i].category;\n+\n+\textract_cmds(&cmds, mask);\n+\n+\tfor (i = 0; cmds[i].name; i++, nr++) {\n+\t\tif (longest < strlen(cmds[i].name))\n+\t\t\tlongest = strlen(cmds[i].name);\n+\t}\n+\tQSORT(cmds, nr, cmd_name_cmp);\n+\n+\tfor (i = 0; catdesc[i].desc; i++) {\n+\t\tuint32_t mask = catdesc[i].category;\n+\t\tconst char *desc = catdesc[i].desc;\n+\n+\t\tprintf(\"\\n%s\\n\", _(desc));\n+\t\tprint_command_list(cmds, mask, longest);\n+\t}\n+\tfree(cmds);\n+}\n+\n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\n \tstruct cmdname *ent;\n@@ -190,42 +291,10 @@ void list_commands(unsigned int colopts,\n \t}\n }\n \n-static int cmd_group_cmp(const void *elem1, const void *elem2)\n-{\n-\tconst struct cmdname_help *e1 = elem1;\n-\tconst struct cmdname_help *e2 = elem2;\n-\n-\tif (e1->group < e2->group)\n-\t\treturn -1;\n-\tif (e1->group > e2->group)\n-\t\treturn 1;\n-\treturn strcmp(e1->name, e2->name);\n-}\n-\n void list_common_cmds_help(void)\n {\n-\tint i, longest = 0;\n-\tint current_grp = -1;\n-\n-\tfor (i = 0; i < ARRAY_SIZE(common_cmds); i++) {\n-\t\tif (longest < strlen(common_cmds[i].name))\n-\t\t\tlongest = strlen(common_cmds[i].name);\n-\t}\n-\n-\tQSORT(common_cmds, ARRAY_SIZE(common_cmds), cmd_group_cmp);\n-\n \tputs(_(\"These are common Git commands used in various situations:\"));\n-\n-\tfor (i = 0; i < ARRAY_SIZE(common_cmds); i++) {\n-\t\tif (common_cmds[i].group != current_grp) {\n-\t\t\tprintf(\"\\n%s\\n\", _(common_cmd_groups[common_cmds[i].group]));\n-\t\t\tcurrent_grp = common_cmds[i].group;\n-\t\t}\n-\n-\t\tprintf(\"   %s   \", common_cmds[i].name);\n-\t\tmput_char(' ', longest - strlen(common_cmds[i].name));\n-\t\tputs(_(common_cmds[i].help));\n-\t}\n+\tprint_cmd_by_category(common_categories);\n }\n \n int is_in_cmdlist(struct cmdnames *c, const char *s)\n@@ -285,6 +354,7 @@ const char *help_unknown_cmd(const char *cmd)\n {\n \tint i, n, best_similarity = 0;\n \tstruct cmdnames main_cmds, other_cmds;\n+\tstruct cmdname_help *common_cmds;\n \n \tmemset(&main_cmds, 0, sizeof(main_cmds));\n \tmemset(&other_cmds, 0, sizeof(other_cmds));\n@@ -299,6 +369,8 @@ const char *help_unknown_cmd(const char *cmd)\n \tQSORT(main_cmds.names, main_cmds.cnt, cmdname_compare);\n \tuniq(&main_cmds);\n \n+\textract_cmds(&common_cmds, common_mask);\n+\n \t/* This abuses cmdname->len for levenshtein distance */\n \tfor (i = 0, n = 0; i < main_cmds.cnt; i++) {\n \t\tint cmp = 0; /* avoid compiler stupidity */\n@@ -313,10 +385,10 @@ const char *help_unknown_cmd(const char *cmd)\n \t\t\tdie(_(bad_interpreter_advice), cmd, cmd);\n \n \t\t/* Does the candidate appear in common_cmds list? */\n-\t\twhile (n < ARRAY_SIZE(common_cmds) &&\n+\t\twhile (common_cmds[n].name &&\n \t\t       (cmp = strcmp(common_cmds[n].name, candidate)) < 0)\n \t\t\tn++;\n-\t\tif ((n < ARRAY_SIZE(common_cmds)) && !cmp) {\n+\t\tif (common_cmds[n].name && !cmp) {\n \t\t\t/* Yes, this is one of the common commands */\n \t\t\tn++; /* use the entry from common_cmds[] */\n \t\t\tif (starts_with(candidate, cmd)) {\n@@ -329,6 +401,7 @@ const char *help_unknown_cmd(const char *cmd)\n \t\tmain_cmds.names[i]->len =\n \t\t\tlevenshtein(cmd, candidate, 0, 2, 1, 3) + 1;\n \t}\n+\tFREE_AND_NULL(common_cmds);\n \n \tQSORT(main_cmds.names, main_cmds.cnt, levenshtein_compare);\n \ndiff --git a/t/t0012-help.sh b/t/t0012-help.sh\nindex 487b92a5de..c096f33505 100755\n--- a/t/t0012-help.sh\n+++ b/t/t0012-help.sh\n@@ -49,6 +49,15 @@ test_expect_success \"--help does not work for guides\" \"\n \ttest_i18ncmp expect actual\n \"\n \n+test_expect_success 'git help' '\n+\tgit help >help.output &&\n+\ttest_i18ngrep \"^   clone  \" help.output &&\n+\ttest_i18ngrep \"^   add    \" help.output &&\n+\ttest_i18ngrep \"^   log    \" help.output &&\n+\ttest_i18ngrep \"^   commit \" help.output &&\n+\ttest_i18ngrep \"^   fetch  \" help.output\n+'\n+\n test_expect_success 'generate builtin list' '\n \tgit --list-builtins >builtins\n '\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348113","messageId":"20180520184009.976-8-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 07/17] completion: implement and use --list-cmds=main,others","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:39:59Z","receivedAt":"2018-05-20T18:40:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is part of the effort to break down and provide commands by\ncategory in machine-readable form. This could be helpful later on when\ncompletion script switches to use --list-cmds for selecting\ncompletable commands. It would be much easier for the user to choose\nto complete _all_ commands instead of the default selection by passing\ndifferent values to --list-cmds in git-completino.bash.\n\nWhile at there, replace \"git help -a\" in git-completion.bash with\n--list-cmds since it's better suited for this task.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git.txt                  |  3 ++-\n contrib/completion/git-completion.bash |  2 +-\n git.c                                  |  4 ++++\n help.c                                 | 32 ++++++++++++++++++++++++++\n help.h                                 |  4 ++++\n 5 files changed, 43 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 2800e3d188..c01477ab5e 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -167,7 +167,8 @@ foo.bar= ...`) sets `foo.bar` to the empty string which `git config\n \tList commands by group. This is an internal/experimental\n \toption and may change or be removed in the future. Supported\n \tgroups are: builtins, parseopt (builtin commands that use\n-\tparse-options).\n+\tparse-options), main (all commands in libexec directory),\n+\tothers (all other commands in `$PATH` that have git- prefix).\n \n GIT COMMANDS\n ------------\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 3556838759..62ca8641f4 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -839,7 +839,7 @@ __git_commands () {\n \tthen\n \t\tprintf \"%s\" \"${GIT_TESTING_COMMAND_COMPLETION}\"\n \telse\n-\t\tgit help -a|egrep '^  [a-zA-Z0-9]'\n+\t\tgit --list-cmds=main,others\n \tfi\n }\n \ndiff --git a/git.c b/git.c\nindex 376a59b97f..10907f7266 100644\n--- a/git.c\n+++ b/git.c\n@@ -56,6 +56,10 @@ static int list_cmds(const char *spec)\n \n \t\tif (match_token(spec, len, \"builtins\"))\n \t\t\tlist_builtins(&list, 0);\n+\t\telse if (match_token(spec, len, \"main\"))\n+\t\t\tlist_all_main_cmds(&list);\n+\t\telse if (match_token(spec, len, \"others\"))\n+\t\t\tlist_all_other_cmds(&list);\n \t\telse\n \t\t\tdie(_(\"unsupported command listing type '%s'\"), spec);\n \t\tspec += len;\ndiff --git a/help.c b/help.c\nindex 2d6a3157f8..d5ce9dfcbb 100644\n--- a/help.c\n+++ b/help.c\n@@ -297,6 +297,38 @@ void list_common_cmds_help(void)\n \tprint_cmd_by_category(common_categories);\n }\n \n+void list_all_main_cmds(struct string_list *list)\n+{\n+\tstruct cmdnames main_cmds, other_cmds;\n+\tint i;\n+\n+\tmemset(&main_cmds, 0, sizeof(main_cmds));\n+\tmemset(&other_cmds, 0, sizeof(other_cmds));\n+\tload_command_list(\"git-\", &main_cmds, &other_cmds);\n+\n+\tfor (i = 0; i < main_cmds.cnt; i++)\n+\t\tstring_list_append(list, main_cmds.names[i]->name);\n+\n+\tclean_cmdnames(&main_cmds);\n+\tclean_cmdnames(&other_cmds);\n+}\n+\n+void list_all_other_cmds(struct string_list *list)\n+{\n+\tstruct cmdnames main_cmds, other_cmds;\n+\tint i;\n+\n+\tmemset(&main_cmds, 0, sizeof(main_cmds));\n+\tmemset(&other_cmds, 0, sizeof(other_cmds));\n+\tload_command_list(\"git-\", &main_cmds, &other_cmds);\n+\n+\tfor (i = 0; i < other_cmds.cnt; i++)\n+\t\tstring_list_append(list, other_cmds.names[i]->name);\n+\n+\tclean_cmdnames(&main_cmds);\n+\tclean_cmdnames(&other_cmds);\n+}\n+\n int is_in_cmdlist(struct cmdnames *c, const char *s)\n {\n \tint i;\ndiff --git a/help.h b/help.h\nindex b21d7c94e8..97e6c0965e 100644\n--- a/help.h\n+++ b/help.h\n@@ -1,6 +1,8 @@\n #ifndef HELP_H\n #define HELP_H\n \n+struct string_list;\n+\n struct cmdnames {\n \tint alloc;\n \tint cnt;\n@@ -17,6 +19,8 @@ static inline void mput_char(char c, unsigned int num)\n }\n \n extern void list_common_cmds_help(void);\n+extern void list_all_main_cmds(struct string_list *list);\n+extern void list_all_other_cmds(struct string_list *list);\n extern const char *help_unknown_cmd(const char *cmd);\n extern void load_command_list(const char *prefix,\n \t\t\t      struct cmdnames *main_cmds,\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348114","messageId":"20180520184009.976-7-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 06/17] git --list-cmds: collect command list in a string_list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:39:58Z","receivedAt":"2018-05-20T18:40:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Instead of printing the command directly one by one, keep them in a\nlist and print at the end. This allows more modification before we\nprint out (e.g. sorting, removing duplicates or even excluding some\nitems).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n git.c | 22 +++++++++++++++++-----\n 1 file changed, 17 insertions(+), 5 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex cd85355d81..376a59b97f 100644\n--- a/git.c\n+++ b/git.c\n@@ -36,7 +36,7 @@ const char git_more_info_string[] =\n \n static int use_pager = -1;\n \n-static void list_builtins(unsigned int exclude_option, char sep);\n+static void list_builtins(struct string_list *list, unsigned int exclude_option);\n \n static int match_token(const char *spec, int len, const char *token)\n {\n@@ -47,18 +47,24 @@ static int match_token(const char *spec, int len, const char *token)\n \n static int list_cmds(const char *spec)\n {\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tint i;\n+\n \twhile (*spec) {\n \t\tconst char *sep = strchrnul(spec, ',');\n \t\tint len = sep - spec;\n \n \t\tif (match_token(spec, len, \"builtins\"))\n-\t\t\tlist_builtins(0, '\\n');\n+\t\t\tlist_builtins(&list, 0);\n \t\telse\n \t\t\tdie(_(\"unsupported command listing type '%s'\"), spec);\n \t\tspec += len;\n \t\tif (*spec == ',')\n \t\t\tspec++;\n \t}\n+\tfor (i = 0; i < list.nr; i++)\n+\t\tputs(list.items[i].string);\n+\tstring_list_clear(&list, 0);\n \treturn 0;\n }\n \n@@ -249,7 +255,13 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\t(*argc)--;\n \t\t} else if (skip_prefix(cmd, \"--list-cmds=\", &cmd)) {\n \t\t\tif (!strcmp(cmd, \"parseopt\")) {\n-\t\t\t\tlist_builtins(NO_PARSEOPT, ' ');\n+\t\t\t\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\t\t\t\tint i;\n+\n+\t\t\t\tlist_builtins(&list, NO_PARSEOPT);\n+\t\t\t\tfor (i = 0; i < list.nr; i++)\n+\t\t\t\t\tprintf(\"%s \", list.items[i].string);\n+\t\t\t\tstring_list_clear(&list, 0);\n \t\t\t\texit(0);\n \t\t\t} else {\n \t\t\t\texit(list_cmds(cmd));\n@@ -533,14 +545,14 @@ int is_builtin(const char *s)\n \treturn !!get_builtin(s);\n }\n \n-static void list_builtins(unsigned int exclude_option, char sep)\n+static void list_builtins(struct string_list *out, unsigned int exclude_option)\n {\n \tint i;\n \tfor (i = 0; i < ARRAY_SIZE(commands); i++) {\n \t\tif (exclude_option &&\n \t\t    (commands[i].option & exclude_option))\n \t\t\tcontinue;\n-\t\tprintf(\"%s%c\", commands[i].cmd, sep);\n+\t\tstring_list_append(out, commands[i].cmd);\n \t}\n }\n \n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348115","messageId":"20180520184009.976-9-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 08/17] git: support --list-cmds=list-<category>","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:00Z","receivedAt":"2018-05-20T18:40:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This allows us to select any group of commands by a category defined\nin command-list.txt. This is an internal/hidden option so we don't\nhave to be picky about the category name or worried about exposing too\nmuch.\n\nThis will be used later by git-completion.bash to retrieve certain\ncommand groups.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git.txt |  3 ++-\n generate-cmdlist.sh   | 17 +++++++++++++++++\n git.c                 |  7 +++++++\n help.c                | 23 +++++++++++++++++++++++\n help.h                |  2 ++\n 5 files changed, 51 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex c01477ab5e..c423810eac 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -168,7 +168,8 @@ foo.bar= ...`) sets `foo.bar` to the empty string which `git config\n \toption and may change or be removed in the future. Supported\n \tgroups are: builtins, parseopt (builtin commands that use\n \tparse-options), main (all commands in libexec directory),\n-\tothers (all other commands in `$PATH` that have git- prefix).\n+\tothers (all other commands in `$PATH` that have git- prefix),\n+\tlist-<category> (see categories in command-list.txt)\n \n GIT COMMANDS\n ------------\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex 3bcc1ee57d..8d6d8b45ce 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -45,6 +45,21 @@ define_categories () {\n \ttest \"$bit\" -gt 32 && die \"Urgh.. too many categories?\"\n }\n \n+define_category_names () {\n+\techo\n+\techo \"/* Category names */\"\n+\techo \"static const char *category_names[] = {\"\n+\tbit=0\n+\tcategory_list \"$1\" |\n+\twhile read cat\n+\tdo\n+\t\techo \"\t\\\"$cat\\\", /* (1UL << $bit) */\"\n+\t\tbit=$(($bit+1))\n+\tdone\n+\techo \"\tNULL\"\n+\techo \"};\"\n+}\n+\n print_command_list () {\n \techo \"static struct cmdname_help command_list[] = {\"\n \n@@ -70,4 +85,6 @@ struct cmdname_help {\n \"\n define_categories \"$1\"\n echo\n+define_category_names \"$1\"\n+echo\n print_command_list \"$1\"\ndiff --git a/git.c b/git.c\nindex 10907f7266..4d5b8a9931 100644\n--- a/git.c\n+++ b/git.c\n@@ -60,6 +60,13 @@ static int list_cmds(const char *spec)\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n \t\t\tlist_all_other_cmds(&list);\n+\t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\t\tstrbuf_add(&sb, spec + 5, len - 5);\n+\t\t\tlist_cmds_by_category(&list, sb.buf);\n+\t\t\tstrbuf_release(&sb);\n+\t\t}\n \t\telse\n \t\t\tdie(_(\"unsupported command listing type '%s'\"), spec);\n \t\tspec += len;\ndiff --git a/help.c b/help.c\nindex d5ce9dfcbb..1117f7d1d1 100644\n--- a/help.c\n+++ b/help.c\n@@ -329,6 +329,29 @@ void list_all_other_cmds(struct string_list *list)\n \tclean_cmdnames(&other_cmds);\n }\n \n+void list_cmds_by_category(struct string_list *list,\n+\t\t\t   const char *cat)\n+{\n+\tint i, n = ARRAY_SIZE(command_list);\n+\tuint32_t cat_id = 0;\n+\n+\tfor (i = 0; category_names[i]; i++) {\n+\t\tif (!strcmp(cat, category_names[i])) {\n+\t\t\tcat_id = 1UL << i;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (!cat_id)\n+\t\tdie(_(\"unsupported command listing type '%s'\"), cat);\n+\n+\tfor (i = 0; i < n; i++) {\n+\t\tstruct cmdname_help *cmd = command_list + i;\n+\n+\t\tif (cmd->category & cat_id)\n+\t\t\tstring_list_append(list, drop_prefix(cmd->name));\n+\t}\n+}\n+\n int is_in_cmdlist(struct cmdnames *c, const char *s)\n {\n \tint i;\ndiff --git a/help.h b/help.h\nindex 97e6c0965e..734bba32d3 100644\n--- a/help.h\n+++ b/help.h\n@@ -21,6 +21,8 @@ static inline void mput_char(char c, unsigned int num)\n extern void list_common_cmds_help(void);\n extern void list_all_main_cmds(struct string_list *list);\n extern void list_all_other_cmds(struct string_list *list);\n+extern void list_cmds_by_category(struct string_list *list,\n+\t\t\t\t  const char *category);\n extern const char *help_unknown_cmd(const char *cmd);\n extern void load_command_list(const char *prefix,\n \t\t\t      struct cmdnames *main_cmds,\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348116","messageId":"20180520184009.976-14-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 13/17] completion: reduce completable command list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:05Z","receivedAt":"2018-05-20T18:40:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The following commands are removed from the complete list:\n\n- annotate             obsolete, discouraged to use\n- filter-branch        not often used\n- get-tar-commit-id    not often used\n- imap-send            not often used\n- interpreter-trailers not for interactive use\n- name-rev             plumbing, just use git-describe\n- p4                   too short and probably not often used (*)\n- svn                  same category as p4 (*)\n- verify-commit        not often used\n\n(*) to be fair, send-email command which is in the same foreignscminterface\ngroup as svn and p4 does get completion, just because it's used by git\nand kernel development. So maybe we should include them.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n command-list.txt | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/command-list.txt b/command-list.txt\nindex dcf1907a54..e505a1e34c 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -47,7 +47,7 @@\n # command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\n git-am                                  mainporcelain\n-git-annotate                            ancillaryinterrogators          complete\n+git-annotate                            ancillaryinterrogators\n git-apply                               plumbingmanipulators            complete\n git-archimport                          foreignscminterface\n git-archive                             mainporcelain\n@@ -89,13 +89,13 @@ git-fast-export                         ancillarymanipulators\n git-fast-import                         ancillarymanipulators\n git-fetch                               mainporcelain           remote\n git-fetch-pack                          synchingrepositories\n-git-filter-branch                       ancillarymanipulators           complete\n+git-filter-branch                       ancillarymanipulators\n git-fmt-merge-msg                       purehelpers\n git-for-each-ref                        plumbinginterrogators\n git-format-patch                        mainporcelain\n git-fsck                                ancillaryinterrogators          complete\n git-gc                                  mainporcelain\n-git-get-tar-commit-id                   ancillaryinterrogators          complete\n+git-get-tar-commit-id                   ancillaryinterrogators\n git-grep                                mainporcelain           info\n git-gui                                 mainporcelain\n git-hash-object                         plumbingmanipulators\n@@ -103,11 +103,11 @@ git-help                                ancillaryinterrogators          complete\n git-http-backend                        synchingrepositories\n git-http-fetch                          synchelpers\n git-http-push                           synchelpers\n-git-imap-send                           foreignscminterface             complete\n+git-imap-send                           foreignscminterface\n git-index-pack                          plumbingmanipulators\n git-init                                mainporcelain           init\n git-instaweb                            ancillaryinterrogators          complete\n-git-interpret-trailers                  purehelpers                     complete\n+git-interpret-trailers                  purehelpers\n gitk                                    mainporcelain\n git-log                                 mainporcelain           info\n git-ls-files                            plumbinginterrogators\n@@ -125,9 +125,9 @@ git-merge-tree                          ancillaryinterrogators\n git-mktag                               plumbingmanipulators\n git-mktree                              plumbingmanipulators\n git-mv                                  mainporcelain           worktree\n-git-name-rev                            plumbinginterrogators           complete\n+git-name-rev                            plumbinginterrogators\n git-notes                               mainporcelain\n-git-p4                                  foreignscminterface             complete\n+git-p4                                  foreignscminterface\n git-pack-objects                        plumbingmanipulators\n git-pack-redundant                      plumbinginterrogators\n git-pack-refs                           ancillarymanipulators\n@@ -167,7 +167,7 @@ git-stage                                                               complete\n git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n-git-svn                                 foreignscminterface             complete\n+git-svn                                 foreignscminterface\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\n@@ -178,7 +178,7 @@ git-update-server-info                  synchingrepositories\n git-upload-archive                      synchelpers\n git-upload-pack                         synchelpers\n git-var                                 plumbinginterrogators\n-git-verify-commit                       ancillaryinterrogators          complete\n+git-verify-commit                       ancillaryinterrogators\n git-verify-pack                         plumbinginterrogators\n git-verify-tag                          ancillaryinterrogators\n gitweb                                  ancillaryinterrogators\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348117","messageId":"20180520184009.976-15-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 14/17] Move declaration for alias.c to alias.h","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:06Z","receivedAt":"2018-05-20T18:40:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n alias.c         | 1 +\n alias.h         | 9 +++++++++\n builtin/help.c  | 1 +\n builtin/merge.c | 1 +\n cache.h         | 5 -----\n connect.c       | 1 +\n git.c           | 1 +\n pager.c         | 1 +\n sequencer.c     | 1 +\n shell.c         | 1 +\n 10 files changed, 17 insertions(+), 5 deletions(-)\n create mode 100644 alias.h\n\ndiff --git a/alias.c b/alias.c\nindex bf146e5263..e9726ce8c5 100644\n--- a/alias.c\n+++ b/alias.c\n@@ -1,4 +1,5 @@\n #include \"cache.h\"\n+#include \"alias.h\"\n #include \"config.h\"\n \n struct config_alias_data {\ndiff --git a/alias.h b/alias.h\nnew file mode 100644\nindex 0000000000..fbf1d22a94\n--- /dev/null\n+++ b/alias.h\n@@ -0,0 +1,9 @@\n+#ifndef __ALIAS_H__\n+#define __ALIAS_H__\n+\n+char *alias_lookup(const char *alias);\n+int split_cmdline(char *cmdline, const char ***argv);\n+/* Takes a negative value returned by split_cmdline */\n+const char *split_cmdline_strerror(int cmdline_errno);\n+\n+#endif\ndiff --git a/builtin/help.c b/builtin/help.c\nindex 5727fb5e51..6b4b3df90d 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -9,6 +9,7 @@\n #include \"run-command.h\"\n #include \"column.h\"\n #include \"help.h\"\n+#include \"alias.h\"\n \n #ifndef DEFAULT_HELP_FORMAT\n #define DEFAULT_HELP_FORMAT \"man\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 9db5a2cf16..e3681cd850 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -34,6 +34,7 @@\n #include \"string-list.h\"\n #include \"packfile.h\"\n #include \"tag.h\"\n+#include \"alias.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\ndiff --git a/cache.h b/cache.h\nindex bbaf5c349a..111116ea13 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1835,11 +1835,6 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule);\n void overlay_tree_on_index(struct index_state *istate,\n \t\t\t   const char *tree_name, const char *prefix);\n \n-char *alias_lookup(const char *alias);\n-int split_cmdline(char *cmdline, const char ***argv);\n-/* Takes a negative value returned by split_cmdline */\n-const char *split_cmdline_strerror(int cmdline_errno);\n-\n /* setup.c */\n struct startup_info {\n \tint have_repository;\ndiff --git a/connect.c b/connect.c\nindex c3a014c5ba..ff078d28dc 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -13,6 +13,7 @@\n #include \"transport.h\"\n #include \"strbuf.h\"\n #include \"protocol.h\"\n+#include \"alias.h\"\n \n static char *server_capabilities;\n static const char *parse_feature_value(const char *, const char *, int *);\ndiff --git a/git.c b/git.c\nindex 4d5b8a9931..19f73b3fa3 100644\n--- a/git.c\n+++ b/git.c\n@@ -3,6 +3,7 @@\n #include \"exec_cmd.h\"\n #include \"help.h\"\n #include \"run-command.h\"\n+#include \"alias.h\"\n \n #define RUN_SETUP\t\t(1<<0)\n #define RUN_SETUP_GENTLY\t(1<<1)\ndiff --git a/pager.c b/pager.c\nindex 92b23e6cd1..1f4688fa03 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"run-command.h\"\n #include \"sigchain.h\"\n+#include \"alias.h\"\n \n #ifndef DEFAULT_PAGER\n #define DEFAULT_PAGER \"less\"\ndiff --git a/sequencer.c b/sequencer.c\nindex 667f35ebdf..1288a36ebd 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -23,6 +23,7 @@\n #include \"hashmap.h\"\n #include \"notes-utils.h\"\n #include \"sigchain.h\"\n+#include \"alias.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \ndiff --git a/shell.c b/shell.c\nindex 234b2d4f16..3ce77b8e34 100644\n--- a/shell.c\n+++ b/shell.c\n@@ -3,6 +3,7 @@\n #include \"exec_cmd.h\"\n #include \"strbuf.h\"\n #include \"run-command.h\"\n+#include \"alias.h\"\n \n #define COMMAND_DIR \"git-shell-commands\"\n #define HELP_COMMAND COMMAND_DIR \"/help\"\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348118","messageId":"20180520184009.976-16-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 15/17] completion: add and use --list-cmds=nohelpers","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:07Z","receivedAt":"2018-05-20T18:40:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git.txt                  |  3 ++-\n contrib/completion/git-completion.bash | 20 ++++----------------\n git.c                                  | 14 ++++++++++++++\n 3 files changed, 20 insertions(+), 17 deletions(-)\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex c423810eac..820ab77fcb 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -169,7 +169,8 @@ foo.bar= ...`) sets `foo.bar` to the empty string which `git config\n \tgroups are: builtins, parseopt (builtin commands that use\n \tparse-options), main (all commands in libexec directory),\n \tothers (all other commands in `$PATH` that have git- prefix),\n-\tlist-<category> (see categories in command-list.txt)\n+\tlist-<category> (see categories in command-list.txt),\n+\tnohelpers (exclude helper commands).\n \n GIT COMMANDS\n ------------\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex cd1d8e553f..217c8a3d3b 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -843,7 +843,7 @@ __git_commands () {\n \t\tthen\n \t\t\tprintf \"%s\" \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n \t\telse\n-\t\t\tgit --list-cmds=list-mainporcelain,others,list-complete\n+\t\t\tgit --list-cmds=list-mainporcelain,others,nohelpers,list-complete\n \t\tfi\n \t\t;;\n \tall)\n@@ -851,27 +851,15 @@ __git_commands () {\n \t\tthen\n \t\t\tprintf \"%s\" \"$GIT_TESTING_ALL_COMMAND_LIST\"\n \t\telse\n-\t\t\tgit --list-cmds=main,others\n+\t\t\tgit --list-cmds=main,others,nohelpers\n \t\tfi\n \t\t;;\n \tesac\n }\n \n-__git_list_commands ()\n-{\n-\tlocal i IFS=\" \"$'\\n'\n-\tfor i in $(__git_commands $1)\n-\tdo\n-\t\tcase $i in\n-\t\t*--*)             : helper pattern;;\n-\t\t*) echo $i;;\n-\t\tesac\n-\tdone\n-}\n-\n __git_list_all_commands ()\n {\n-\t__git_list_commands all\n+\t__git_commands all\n }\n \n __git_all_commands=\n@@ -883,7 +871,7 @@ __git_compute_all_commands ()\n \n __git_list_porcelain_commands ()\n {\n-\t__git_list_commands porcelain\n+\t__git_commands porcelain\n }\n \n __git_porcelain_commands=\ndiff --git a/git.c b/git.c\nindex 19f73b3fa3..f6ae79ffaf 100644\n--- a/git.c\n+++ b/git.c\n@@ -39,6 +39,18 @@ static int use_pager = -1;\n \n static void list_builtins(struct string_list *list, unsigned int exclude_option);\n \n+static void exclude_helpers_from_list(struct string_list *list)\n+{\n+\tint i = 0;\n+\n+\twhile (i < list->nr) {\n+\t\tif (strstr(list->items[i].string, \"--\"))\n+\t\t\tunsorted_string_list_delete_item(list, i, 0);\n+\t\telse\n+\t\t\ti++;\n+\t}\n+}\n+\n static int match_token(const char *spec, int len, const char *token)\n {\n \tint token_len = strlen(token);\n@@ -61,6 +73,8 @@ static int list_cmds(const char *spec)\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n \t\t\tlist_all_other_cmds(&list);\n+\t\telse if (match_token(spec, len, \"nohelpers\"))\n+\t\t\texclude_helpers_from_list(&list);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348119","messageId":"20180520184009.976-13-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 12/17] completion: let git provide the completable command list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:04Z","receivedAt":"2018-05-20T18:40:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Instead of maintaining a separate list of command classification,\nwhich often could go out of date, let's centralize the information\nback in git.\n\nWhile the function in git-completion.bash implies \"list porcelain\ncommands\", that's not exactly what it does. It gets all commands (aka\n--list-cmds=main,others) then exclude certain non-porcelain ones. We\ncould almost recreate this list two lists list-mainporcelain and\nothers. The non-porcelain-but-included-anyway is added by the third\ncategory list-complete.\n\nNote that the current completion script incorrectly classifies\nfilter-branch as porcelain and t9902 tests this behavior. We keep it\nthis way in t9902 because this test does not really care which\nparticular command is porcelain or plumbing, they're just names.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n command-list.txt                       |  53 +++++------\n contrib/completion/git-completion.bash | 119 ++++++-------------------\n t/t9902-completion.sh                  |   5 +-\n 3 files changed, 58 insertions(+), 119 deletions(-)\n\ndiff --git a/command-list.txt b/command-list.txt\nindex a2f360eab9..dcf1907a54 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -47,12 +47,12 @@\n # command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\n git-am                                  mainporcelain\n-git-annotate                            ancillaryinterrogators\n-git-apply                               plumbingmanipulators\n+git-annotate                            ancillaryinterrogators          complete\n+git-apply                               plumbingmanipulators            complete\n git-archimport                          foreignscminterface\n git-archive                             mainporcelain\n git-bisect                              mainporcelain           info\n-git-blame                               ancillaryinterrogators\n+git-blame                               ancillaryinterrogators          complete\n git-branch                              mainporcelain           history\n git-bundle                              mainporcelain\n git-cat-file                            plumbinginterrogators\n@@ -62,7 +62,7 @@ git-check-mailmap                       purehelpers\n git-checkout                            mainporcelain           history\n git-checkout-index                      plumbingmanipulators\n git-check-ref-format                    purehelpers\n-git-cherry                              ancillaryinterrogators\n+git-cherry                              ancillaryinterrogators          complete\n git-cherry-pick                         mainporcelain\n git-citool                              mainporcelain\n git-clean                               mainporcelain\n@@ -70,7 +70,7 @@ git-clone                               mainporcelain           init\n git-column                              purehelpers\n git-commit                              mainporcelain           history\n git-commit-tree                         plumbingmanipulators\n-git-config                              ancillarymanipulators\n+git-config                              ancillarymanipulators           complete\n git-count-objects                       ancillaryinterrogators\n git-credential                          purehelpers\n git-credential-cache                    purehelpers\n@@ -84,30 +84,30 @@ git-diff                                mainporcelain           history\n git-diff-files                          plumbinginterrogators\n git-diff-index                          plumbinginterrogators\n git-diff-tree                           plumbinginterrogators\n-git-difftool                            ancillaryinterrogators\n+git-difftool                            ancillaryinterrogators          complete\n git-fast-export                         ancillarymanipulators\n git-fast-import                         ancillarymanipulators\n git-fetch                               mainporcelain           remote\n git-fetch-pack                          synchingrepositories\n-git-filter-branch                       ancillarymanipulators\n+git-filter-branch                       ancillarymanipulators           complete\n git-fmt-merge-msg                       purehelpers\n git-for-each-ref                        plumbinginterrogators\n git-format-patch                        mainporcelain\n-git-fsck                                ancillaryinterrogators\n+git-fsck                                ancillaryinterrogators          complete\n git-gc                                  mainporcelain\n-git-get-tar-commit-id                   ancillaryinterrogators\n+git-get-tar-commit-id                   ancillaryinterrogators          complete\n git-grep                                mainporcelain           info\n git-gui                                 mainporcelain\n git-hash-object                         plumbingmanipulators\n-git-help                                ancillaryinterrogators\n+git-help                                ancillaryinterrogators          complete\n git-http-backend                        synchingrepositories\n git-http-fetch                          synchelpers\n git-http-push                           synchelpers\n-git-imap-send                           foreignscminterface\n+git-imap-send                           foreignscminterface             complete\n git-index-pack                          plumbingmanipulators\n git-init                                mainporcelain           init\n-git-instaweb                            ancillaryinterrogators\n-git-interpret-trailers                  purehelpers\n+git-instaweb                            ancillaryinterrogators          complete\n+git-interpret-trailers                  purehelpers                     complete\n gitk                                    mainporcelain\n git-log                                 mainporcelain           info\n git-ls-files                            plumbinginterrogators\n@@ -120,14 +120,14 @@ git-merge-base                          plumbinginterrogators\n git-merge-file                          plumbingmanipulators\n git-merge-index                         plumbingmanipulators\n git-merge-one-file                      purehelpers\n-git-mergetool                           ancillarymanipulators\n+git-mergetool                           ancillarymanipulators           complete\n git-merge-tree                          ancillaryinterrogators\n git-mktag                               plumbingmanipulators\n git-mktree                              plumbingmanipulators\n git-mv                                  mainporcelain           worktree\n-git-name-rev                            plumbinginterrogators\n+git-name-rev                            plumbinginterrogators           complete\n git-notes                               mainporcelain\n-git-p4                                  foreignscminterface\n+git-p4                                  foreignscminterface             complete\n git-pack-objects                        plumbingmanipulators\n git-pack-redundant                      plumbinginterrogators\n git-pack-refs                           ancillarymanipulators\n@@ -141,32 +141,33 @@ git-quiltimport                         foreignscminterface\n git-read-tree                           plumbingmanipulators\n git-rebase                              mainporcelain           history\n git-receive-pack                        synchelpers\n-git-reflog                              ancillarymanipulators\n-git-remote                              ancillarymanipulators\n-git-repack                              ancillarymanipulators\n-git-replace                             ancillarymanipulators\n-git-request-pull                        foreignscminterface\n+git-reflog                              ancillarymanipulators           complete\n+git-remote                              ancillarymanipulators           complete\n+git-repack                              ancillarymanipulators           complete\n+git-replace                             ancillarymanipulators           complete\n+git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           worktree\n git-revert                              mainporcelain\n git-rev-list                            plumbinginterrogators\n git-rev-parse                           ancillaryinterrogators\n git-rm                                  mainporcelain           worktree\n-git-send-email                          foreignscminterface\n+git-send-email                          foreignscminterface             complete\n git-send-pack                           synchingrepositories\n git-shell                               synchelpers\n git-shortlog                            mainporcelain\n git-show                                mainporcelain           info\n-git-show-branch                         ancillaryinterrogators\n+git-show-branch                         ancillaryinterrogators          complete\n git-show-index                          plumbinginterrogators\n git-show-ref                            plumbinginterrogators\n git-sh-i18n                             purehelpers\n git-sh-setup                            purehelpers\n git-stash                               mainporcelain\n+git-stage                                                               complete\n git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n-git-svn                                 foreignscminterface\n+git-svn                                 foreignscminterface             complete\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\n@@ -177,11 +178,11 @@ git-update-server-info                  synchingrepositories\n git-upload-archive                      synchelpers\n git-upload-pack                         synchelpers\n git-var                                 plumbinginterrogators\n-git-verify-commit                       ancillaryinterrogators\n+git-verify-commit                       ancillaryinterrogators          complete\n git-verify-pack                         plumbinginterrogators\n git-verify-tag                          ancillaryinterrogators\n gitweb                                  ancillaryinterrogators\n-git-whatchanged                         ancillaryinterrogators\n+git-whatchanged                         ancillaryinterrogators          complete\n git-worktree                            mainporcelain\n git-write-tree                          plumbingmanipulators\n gitattributes                           guide\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 4e724a5b76..cd1d8e553f 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -834,19 +834,33 @@ __git_complete_strategy ()\n \treturn 1\n }\n \n+# __git_commands requires 1 argument:\n+# 1: the command group, either \"all\" or \"porcelain\"\n __git_commands () {\n-\tif test -n \"${GIT_TESTING_COMMAND_COMPLETION:-}\"\n-\tthen\n-\t\tprintf \"%s\" \"${GIT_TESTING_COMMAND_COMPLETION}\"\n-\telse\n-\t\tgit --list-cmds=main,others\n-\tfi\n+\tcase \"$1\" in\n+\tporcelain)\n+\t\tif test -n \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n+\t\tthen\n+\t\t\tprintf \"%s\" \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n+\t\telse\n+\t\t\tgit --list-cmds=list-mainporcelain,others,list-complete\n+\t\tfi\n+\t\t;;\n+\tall)\n+\t\tif test -n \"$GIT_TESTING_ALL_COMMAND_LIST\"\n+\t\tthen\n+\t\t\tprintf \"%s\" \"$GIT_TESTING_ALL_COMMAND_LIST\"\n+\t\telse\n+\t\t\tgit --list-cmds=main,others\n+\t\tfi\n+\t\t;;\n+\tesac\n }\n \n-__git_list_all_commands ()\n+__git_list_commands ()\n {\n \tlocal i IFS=\" \"$'\\n'\n-\tfor i in $(__git_commands)\n+\tfor i in $(__git_commands $1)\n \tdo\n \t\tcase $i in\n \t\t*--*)             : helper pattern;;\n@@ -855,6 +869,11 @@ __git_list_all_commands ()\n \tdone\n }\n \n+__git_list_all_commands ()\n+{\n+\t__git_list_commands all\n+}\n+\n __git_all_commands=\n __git_compute_all_commands ()\n {\n@@ -864,89 +883,7 @@ __git_compute_all_commands ()\n \n __git_list_porcelain_commands ()\n {\n-\tlocal i IFS=\" \"$'\\n'\n-\t__git_compute_all_commands\n-\tfor i in $__git_all_commands\n-\tdo\n-\t\tcase $i in\n-\t\t*--*)             : helper pattern;;\n-\t\tapplymbox)        : ask gittus;;\n-\t\tapplypatch)       : ask gittus;;\n-\t\tarchimport)       : import;;\n-\t\tcat-file)         : plumbing;;\n-\t\tcheck-attr)       : plumbing;;\n-\t\tcheck-ignore)     : plumbing;;\n-\t\tcheck-mailmap)    : plumbing;;\n-\t\tcheck-ref-format) : plumbing;;\n-\t\tcheckout-index)   : plumbing;;\n-\t\tcolumn)           : internal helper;;\n-\t\tcommit-tree)      : plumbing;;\n-\t\tcount-objects)    : infrequent;;\n-\t\tcredential)       : credentials;;\n-\t\tcredential-*)     : credentials helper;;\n-\t\tcvsexportcommit)  : export;;\n-\t\tcvsimport)        : import;;\n-\t\tcvsserver)        : daemon;;\n-\t\tdaemon)           : daemon;;\n-\t\tdiff-files)       : plumbing;;\n-\t\tdiff-index)       : plumbing;;\n-\t\tdiff-tree)        : plumbing;;\n-\t\tfast-import)      : import;;\n-\t\tfast-export)      : export;;\n-\t\tfsck-objects)     : plumbing;;\n-\t\tfetch-pack)       : plumbing;;\n-\t\tfmt-merge-msg)    : plumbing;;\n-\t\tfor-each-ref)     : plumbing;;\n-\t\thash-object)      : plumbing;;\n-\t\thttp-*)           : transport;;\n-\t\tindex-pack)       : plumbing;;\n-\t\tinit-db)          : deprecated;;\n-\t\tlocal-fetch)      : plumbing;;\n-\t\tls-files)         : plumbing;;\n-\t\tls-remote)        : plumbing;;\n-\t\tls-tree)          : plumbing;;\n-\t\tmailinfo)         : plumbing;;\n-\t\tmailsplit)        : plumbing;;\n-\t\tmerge-*)          : plumbing;;\n-\t\tmktree)           : plumbing;;\n-\t\tmktag)            : plumbing;;\n-\t\tpack-objects)     : plumbing;;\n-\t\tpack-redundant)   : plumbing;;\n-\t\tpack-refs)        : plumbing;;\n-\t\tparse-remote)     : plumbing;;\n-\t\tpatch-id)         : plumbing;;\n-\t\tprune)            : plumbing;;\n-\t\tprune-packed)     : plumbing;;\n-\t\tquiltimport)      : import;;\n-\t\tread-tree)        : plumbing;;\n-\t\treceive-pack)     : plumbing;;\n-\t\tremote-*)         : transport;;\n-\t\trerere)           : plumbing;;\n-\t\trev-list)         : plumbing;;\n-\t\trev-parse)        : plumbing;;\n-\t\trunstatus)        : plumbing;;\n-\t\tsh-setup)         : internal;;\n-\t\tshell)            : daemon;;\n-\t\tshow-ref)         : plumbing;;\n-\t\tsend-pack)        : plumbing;;\n-\t\tshow-index)       : plumbing;;\n-\t\tssh-*)            : transport;;\n-\t\tstripspace)       : plumbing;;\n-\t\tsymbolic-ref)     : plumbing;;\n-\t\tunpack-file)      : plumbing;;\n-\t\tunpack-objects)   : plumbing;;\n-\t\tupdate-index)     : plumbing;;\n-\t\tupdate-ref)       : plumbing;;\n-\t\tupdate-server-info) : daemon;;\n-\t\tupload-archive)   : plumbing;;\n-\t\tupload-pack)      : plumbing;;\n-\t\twrite-tree)       : plumbing;;\n-\t\tvar)              : infrequent;;\n-\t\tverify-pack)      : infrequent;;\n-\t\tverify-tag)       : plumbing;;\n-\t\t*) echo $i;;\n-\t\tesac\n-\tdone\n+\t__git_list_commands porcelain\n }\n \n __git_porcelain_commands=\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 1b34caa1e1..2f16679380 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -13,7 +13,7 @@ complete ()\n \treturn 0\n }\n \n-# Be careful when updating this list:\n+# Be careful when updating these lists:\n #\n # (1) The build tree may have build artifact from different branch, or\n #     the user's $PATH may have a random executable that may begin\n@@ -30,7 +30,8 @@ complete ()\n #     completion for \"git <TAB>\", and a plumbing is excluded.  \"add\",\n #     \"filter-branch\" and \"ls-files\" are listed for this.\n \n-GIT_TESTING_COMMAND_COMPLETION='add checkout check-attr filter-branch ls-files'\n+GIT_TESTING_ALL_COMMAND_LIST='add checkout check-attr filter-branch ls-files'\n+GIT_TESTING_PORCELAIN_COMMAND_LIST='add checkout filter-branch'\n \n . \"$GIT_BUILD_DIR/contrib/completion/git-completion.bash\"\n \n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348120","messageId":"20180520184009.976-12-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 11/17] command-list.txt: documentation and guide line","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:03Z","receivedAt":"2018-05-20T18:40:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is intended to help anybody who needs to update command-list.txt.\nIt gives a brief introduction of all attributes a command can take.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n command-list.txt | 45 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 45 insertions(+)\n\ndiff --git a/command-list.txt b/command-list.txt\nindex 99ddc231c1..a2f360eab9 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -1,3 +1,48 @@\n+# Command classification list\n+# ---------------------------\n+# All supported commands, builtin or external, must be described in\n+# here. This info is used to list commands in various places. Each\n+# command is on one line followed by one or more attributes.\n+#\n+# The first attribute group is mandatory and indicates the command\n+# type. This group includes:\n+#\n+#   mainporcelain\n+#   ancillarymanipulators\n+#   ancillaryinterrogators\n+#   foreignscminterface\n+#   plumbingmanipulators\n+#   plumbinginterrogators\n+#   synchingrepositories\n+#   synchelpers\n+#   purehelpers\n+#\n+# The type names are self explanatory. But if you want to see what\n+# command belongs to what group to get a better picture, have a look\n+# at \"git\" man page, \"GIT COMMANDS\" section.\n+#\n+# Commands of type mainporcelain can also optionally have one of these\n+# attributes:\n+#\n+#   init\n+#   worktree\n+#   info\n+#   history\n+#   remote\n+#\n+# These commands are considered \"common\" and will show up in \"git\n+# help\" output in groups. Uncommon porcelain commands must not\n+# specify any of these attributes.\n+#\n+# \"complete\" attribute is used to mark that the command should be\n+# completable by git-completion.bash. Note that by default,\n+# mainporcelain commands are completable so you don't need this\n+# attribute.\n+#\n+# As part of the Git man page list, the man(5/7) guides are also\n+# specified here, which can only have \"guide\" attribute and nothing\n+# else.\n+#\n ### command list (do not change this line, also do not change alignment)\n # command name                          category [category] [category]\n git-add                                 mainporcelain           worktree\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348121","messageId":"20180520184009.976-11-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 10/17] help: use command-list.txt for the source of guides","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:02Z","receivedAt":"2018-05-20T18:40:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The help command currently hard codes the list of guides and their\nsummary in C. Let's move this list to command-list.txt. This lets us\nextract summary lines from Documentation/git*.txt. This also\npotentially lets us list guides in git.txt, but I'll leave that for\nnow.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/gitattributes.txt        |  2 +-\n Documentation/gitmodules.txt           |  2 +-\n Documentation/gitrevisions.txt         |  2 +-\n Makefile                               |  2 +-\n builtin/help.c                         | 32 --------------------------\n command-list.txt                       | 16 +++++++++++++\n contrib/completion/git-completion.bash | 15 ++++++++----\n help.c                                 | 21 +++++++++++++----\n help.h                                 |  1 +\n t/t0012-help.sh                        |  6 +++++\n 10 files changed, 54 insertions(+), 45 deletions(-)\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 1094fe2b5b..083c2f380d 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -3,7 +3,7 @@ gitattributes(5)\n \n NAME\n ----\n-gitattributes - defining attributes per path\n+gitattributes - Defining attributes per path\n \n SYNOPSIS\n --------\ndiff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt\nindex db5d47eb19..4d63def206 100644\n--- a/Documentation/gitmodules.txt\n+++ b/Documentation/gitmodules.txt\n@@ -3,7 +3,7 @@ gitmodules(5)\n \n NAME\n ----\n-gitmodules - defining submodule properties\n+gitmodules - Defining submodule properties\n \n SYNOPSIS\n --------\ndiff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt\nindex 27dec5b91d..1f6cceaefb 100644\n--- a/Documentation/gitrevisions.txt\n+++ b/Documentation/gitrevisions.txt\n@@ -3,7 +3,7 @@ gitrevisions(7)\n \n NAME\n ----\n-gitrevisions - specifying revisions and ranges for Git\n+gitrevisions - Specifying revisions and ranges for Git\n \n SYNOPSIS\n --------\ndiff --git a/Makefile b/Makefile\nindex a60a78ee67..1efb751e46 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1937,7 +1937,7 @@ $(BUILT_INS): git$X\n \n command-list.h: generate-cmdlist.sh command-list.txt\n \n-command-list.h: $(wildcard Documentation/git-*.txt)\n+command-list.h: $(wildcard Documentation/git*.txt)\n \t$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@\n \n SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\\\ndiff --git a/builtin/help.c b/builtin/help.c\nindex 0e0af8426a..5727fb5e51 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -402,38 +402,6 @@ static void show_html_page(const char *git_cmd)\n \topen_html(page_path.buf);\n }\n \n-static struct {\n-\tconst char *name;\n-\tconst char *help;\n-} common_guides[] = {\n-\t{ \"attributes\", N_(\"Defining attributes per path\") },\n-\t{ \"everyday\", N_(\"Everyday Git With 20 Commands Or So\") },\n-\t{ \"glossary\", N_(\"A Git glossary\") },\n-\t{ \"ignore\", N_(\"Specifies intentionally untracked files to ignore\") },\n-\t{ \"modules\", N_(\"Defining submodule properties\") },\n-\t{ \"revisions\", N_(\"Specifying revisions and ranges for Git\") },\n-\t{ \"tutorial\", N_(\"A tutorial introduction to Git (for version 1.5.1 or newer)\") },\n-\t{ \"workflows\", N_(\"An overview of recommended workflows with Git\") },\n-};\n-\n-static void list_common_guides_help(void)\n-{\n-\tint i, longest = 0;\n-\n-\tfor (i = 0; i < ARRAY_SIZE(common_guides); i++) {\n-\t\tif (longest < strlen(common_guides[i].name))\n-\t\t\tlongest = strlen(common_guides[i].name);\n-\t}\n-\n-\tputs(_(\"The common Git guides are:\\n\"));\n-\tfor (i = 0; i < ARRAY_SIZE(common_guides); i++) {\n-\t\tprintf(\"   %s   \", common_guides[i].name);\n-\t\tmput_char(' ', longest - strlen(common_guides[i].name));\n-\t\tputs(_(common_guides[i].help));\n-\t}\n-\tputchar('\\n');\n-}\n-\n static const char *check_git_cmd(const char* cmd)\n {\n \tchar *alias;\ndiff --git a/command-list.txt b/command-list.txt\nindex 3bd23201a6..99ddc231c1 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -139,3 +139,19 @@ gitweb                                  ancillaryinterrogators\n git-whatchanged                         ancillaryinterrogators\n git-worktree                            mainporcelain\n git-write-tree                          plumbingmanipulators\n+gitattributes                           guide\n+gitcli                                  guide\n+gitcore-tutorial                        guide\n+gitcvs-migration                        guide\n+gitdiffcore                             guide\n+giteveryday                             guide\n+gitglossary                             guide\n+githooks                                guide\n+gitignore                               guide\n+gitmodules                              guide\n+gitnamespaces                           guide\n+gitrepository-layout                    guide\n+gitrevisions                            guide\n+gittutorial-2                           guide\n+gittutorial                             guide\n+gitworkflows                            guide\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 62ca8641f4..4e724a5b76 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1575,6 +1575,13 @@ _git_grep ()\n \t__git_complete_refs\n }\n \n+__git_all_guides=\n+__git_compute_all_guides ()\n+{\n+\ttest -n \"$__git_all_guides\" ||\n+\t__git_all_guides=$(git --list-cmds=list-guide)\n+}\n+\n _git_help ()\n {\n \tcase \"$cur\" in\n@@ -1584,11 +1591,9 @@ _git_help ()\n \t\t;;\n \tesac\n \t__git_compute_all_commands\n-\t__gitcomp \"$__git_all_commands $(__git_aliases)\n-\t\tattributes cli core-tutorial cvs-migration\n-\t\tdiffcore everyday gitk glossary hooks ignore modules\n-\t\tnamespaces repository-layout revisions tutorial tutorial-2\n-\t\tworkflows\n+\t__git_compute_all_guides\n+\t__gitcomp \"$__git_all_commands $(__git_aliases) $__git_all_guides\n+\t\tgitk\n \t\t\"\n }\n \ndiff --git a/help.c b/help.c\nindex c7df1d2338..23924dd300 100644\n--- a/help.c\n+++ b/help.c\n@@ -39,12 +39,14 @@ static struct category_description main_categories[] = {\n \t{ 0, NULL }\n };\n \n-static const char *drop_prefix(const char *name)\n+static const char *drop_prefix(const char *name, uint32_t category)\n {\n \tconst char *new_name;\n \n \tif (skip_prefix(name, \"git-\", &new_name))\n \t\treturn new_name;\n+\tif (category == CAT_guide && skip_prefix(name, \"git\", &new_name))\n+\t\treturn new_name;\n \treturn name;\n \n }\n@@ -66,7 +68,7 @@ static void extract_cmds(struct cmdname_help **p_cmds, uint32_t mask)\n \t\t\tcontinue;\n \n \t\tcmds[nr] = *cmd;\n-\t\tcmds[nr].name = drop_prefix(cmd->name);\n+\t\tcmds[nr].name = drop_prefix(cmd->name, cmd->category);\n \n \t\tnr++;\n \t}\n@@ -358,11 +360,22 @@ void list_cmds_by_category(struct string_list *list,\n \tfor (i = 0; i < n; i++) {\n \t\tstruct cmdname_help *cmd = command_list + i;\n \n-\t\tif (cmd->category & cat_id)\n-\t\t\tstring_list_append(list, drop_prefix(cmd->name));\n+\t\tif (!(cmd->category & cat_id))\n+\t\t\tcontinue;\n+\t\tstring_list_append(list, drop_prefix(cmd->name, cmd->category));\n \t}\n }\n \n+void list_common_guides_help(void)\n+{\n+\tstruct category_description catdesc[] = {\n+\t\t{ CAT_guide, N_(\"The common Git guides are:\") },\n+\t\t{ 0, NULL }\n+\t};\n+\tprint_cmd_by_category(catdesc);\n+\tputchar('\\n');\n+}\n+\n void list_all_cmds_help(void)\n {\n \tprint_cmd_by_category(main_categories);\ndiff --git a/help.h b/help.h\nindex 40917fc38c..b2293e99be 100644\n--- a/help.h\n+++ b/help.h\n@@ -20,6 +20,7 @@ static inline void mput_char(char c, unsigned int num)\n \n extern void list_common_cmds_help(void);\n extern void list_all_cmds_help(void);\n+extern void list_common_guides_help(void);\n \n extern void list_all_main_cmds(struct string_list *list);\n extern void list_all_other_cmds(struct string_list *list);\ndiff --git a/t/t0012-help.sh b/t/t0012-help.sh\nindex 060df24c2d..bc27df7f38 100755\n--- a/t/t0012-help.sh\n+++ b/t/t0012-help.sh\n@@ -66,6 +66,12 @@ test_expect_success 'git help' '\n \ttest_i18ngrep \"^   commit \" help.output &&\n \ttest_i18ngrep \"^   fetch  \" help.output\n '\n+test_expect_success 'git help -g' '\n+\tgit help -g >help.output &&\n+\ttest_i18ngrep \"^   attributes \" help.output &&\n+\ttest_i18ngrep \"^   everyday   \" help.output &&\n+\ttest_i18ngrep \"^   tutorial   \" help.output\n+'\n \n test_expect_success 'generate builtin list' '\n \tgit --list-cmds=builtins >builtins\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348122","messageId":"20180520184009.976-18-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 17/17] completion: allow to customize the completable command list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:09Z","receivedAt":"2018-05-20T18:41:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"By default we show porcelain, external commands and a couple others\nthat are also popular. If you are not happy with this list, you can\nnow customize it a new config variable.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt               |  8 +++++++\n Documentation/git.txt                  |  3 ++-\n contrib/completion/git-completion.bash |  2 +-\n git.c                                  |  2 ++\n help.c                                 | 33 ++++++++++++++++++++++++++\n help.h                                 |  1 +\n 6 files changed, 47 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2659153cb3..9e81dcf867 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1343,6 +1343,14 @@ credential.<url>.*::\n credentialCache.ignoreSIGHUP::\n \tTell git-credential-cache--daemon to ignore SIGHUP, instead of quitting.\n \n+completion.commands::\n+\tThis is only used by git-completion.bash to add or remove\n+\tcommands from the list of completed commands. Normally only\n+\tporcelain commands and a few select others are completed. You\n+\tcan add more commands, separated by space, in this\n+\tvariable. Prefixing the command with '-' will remove it from\n+\tthe existing list.\n+\n include::diff-config.txt[]\n \n difftool.<tool>.path::\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 75f50d2379..6f7eddf847 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -170,7 +170,8 @@ foo.bar= ...`) sets `foo.bar` to the empty string which `git config\n \tparse-options), main (all commands in libexec directory),\n \tothers (all other commands in `$PATH` that have git- prefix),\n \tlist-<category> (see categories in command-list.txt),\n-\tnohelpers (exclude helper commands) and alias.\n+\tnohelpers (exclude helper commands), alias and config\n+\t(retrieve command list from config variable completion.commands)\n \n GIT COMMANDS\n ------------\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 98f278fb9a..e5b2ccbdd2 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -3012,7 +3012,7 @@ __git_main ()\n \t\t\tthen\n \t\t\t\t__gitcomp \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n \t\t\telse\n-\t\t\t\t__gitcomp \"$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete)\"\n+\t\t\t\t__gitcomp \"$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)\"\n \t\t\tfi\n \t\t\t;;\n \t\tesac\ndiff --git a/git.c b/git.c\nindex 63acd9ea81..447dac0e71 100644\n--- a/git.c\n+++ b/git.c\n@@ -77,6 +77,8 @@ static int list_cmds(const char *spec)\n \t\t\texclude_helpers_from_list(&list);\n \t\telse if (match_token(spec, len, \"alias\"))\n \t\t\tlist_aliases(&list);\n+\t\telse if (match_token(spec, len, \"config\"))\n+\t\t\tlist_cmds_by_config(&list);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \ndiff --git a/help.c b/help.c\nindex 23924dd300..abf87205b2 100644\n--- a/help.c\n+++ b/help.c\n@@ -366,6 +366,39 @@ void list_cmds_by_category(struct string_list *list,\n \t}\n }\n \n+void list_cmds_by_config(struct string_list *list)\n+{\n+\tconst char *cmd_list;\n+\n+\t/*\n+\t * There's no actual repository setup at this point (and even\n+\t * if there is, we don't really care; only global config\n+\t * matters). If we accidentally set up a repository, it's ok\n+\t * too since the caller (git --list-cmds=) should exit shortly\n+\t * anyway.\n+\t */\n+\tif (git_config_get_string_const(\"completion.commands\", &cmd_list))\n+\t\treturn;\n+\n+\tstring_list_sort(list);\n+\tstring_list_remove_duplicates(list, 0);\n+\n+\twhile (*cmd_list) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tconst char *p = strchrnul(cmd_list, ' ');\n+\n+\t\tstrbuf_add(&sb, cmd_list, p - cmd_list);\n+\t\tif (*cmd_list == '-')\n+\t\t\tstring_list_remove(list, cmd_list + 1, 0);\n+\t\telse\n+\t\t\tstring_list_insert(list, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\twhile (*p == ' ')\n+\t\t\tp++;\n+\t\tcmd_list = p;\n+\t}\n+}\n+\n void list_common_guides_help(void)\n {\n \tstruct category_description catdesc[] = {\ndiff --git a/help.h b/help.h\nindex b2293e99be..3b38292a1b 100644\n--- a/help.h\n+++ b/help.h\n@@ -26,6 +26,7 @@ extern void list_all_main_cmds(struct string_list *list);\n extern void list_all_other_cmds(struct string_list *list);\n extern void list_cmds_by_category(struct string_list *list,\n \t\t\t\t  const char *category);\n+extern void list_cmds_by_config(struct string_list *list);\n extern const char *help_unknown_cmd(const char *cmd);\n extern void load_command_list(const char *prefix,\n \t\t\t      struct cmdnames *main_cmds,\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348123","messageId":"20180520184009.976-17-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 16/17] completion: add and use --list-cmds=alias","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:08Z","receivedAt":"2018-05-20T18:41:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"By providing aliases via --list-cmds=, we could simplify command\ncollection code in the script. We only issue one git command. Before\nthis patch that is \"git config\", after it's \"git --list-cmds=\". In\n\"git help\" completion case we actually reduce one \"git\" process (for\ngetting guides) but that call was added in this series so it does not\nreally count.\n\nA couple of bash functions are removed because they are not needed\nanymore. __git_compute_all_commands() and $__git_all_commands stay\nbecause they are still needed for completing pager.* config and\nwithout \"alias\" group, the result is still cacheable.\n\nThere is a slight (good) change in _git_help() with this patch: before\n\"git help <tab>\" shows external commands (as in _not_ part of git) as\nwell as part of $__git_all_commands. We have finer control over\ncommand listing now and can exclude that because we can't provide a\nman page for external commands anyway.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git.txt                  |  2 +-\n alias.c                                | 21 +++++++-\n alias.h                                |  3 ++\n contrib/completion/git-completion.bash | 75 ++++++--------------------\n git.c                                  |  2 +\n t/t9902-completion.sh                  | 18 -------\n 6 files changed, 40 insertions(+), 81 deletions(-)\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 820ab77fcb..75f50d2379 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -170,7 +170,7 @@ foo.bar= ...`) sets `foo.bar` to the empty string which `git config\n \tparse-options), main (all commands in libexec directory),\n \tothers (all other commands in `$PATH` that have git- prefix),\n \tlist-<category> (see categories in command-list.txt),\n-\tnohelpers (exclude helper commands).\n+\tnohelpers (exclude helper commands) and alias.\n \n GIT COMMANDS\n ------------\ndiff --git a/alias.c b/alias.c\nindex e9726ce8c5..a7e4e57130 100644\n--- a/alias.c\n+++ b/alias.c\n@@ -1,10 +1,12 @@\n #include \"cache.h\"\n #include \"alias.h\"\n #include \"config.h\"\n+#include \"string-list.h\"\n \n struct config_alias_data {\n \tconst char *alias;\n \tchar *v;\n+\tstruct string_list *list;\n };\n \n static int config_alias_cb(const char *key, const char *value, void *d)\n@@ -12,8 +14,16 @@ static int config_alias_cb(const char *key, const char *value, void *d)\n \tstruct config_alias_data *data = d;\n \tconst char *p;\n \n-\tif (skip_prefix(key, \"alias.\", &p) && !strcasecmp(p, data->alias))\n-\t\treturn git_config_string((const char **)&data->v, key, value);\n+\tif (!skip_prefix(key, \"alias.\", &p))\n+\t\treturn 0;\n+\n+\tif (data->alias) {\n+\t\tif (!strcasecmp(p, data->alias))\n+\t\t\treturn git_config_string((const char **)&data->v,\n+\t\t\t\t\t\t key, value);\n+\t} else if (data->list) {\n+\t\tstring_list_append(data->list, p);\n+\t}\n \n \treturn 0;\n }\n@@ -27,6 +37,13 @@ char *alias_lookup(const char *alias)\n \treturn data.v;\n }\n \n+void list_aliases(struct string_list *list)\n+{\n+\tstruct config_alias_data data = { NULL, NULL, list };\n+\n+\tread_early_config(config_alias_cb, &data);\n+}\n+\n #define SPLIT_CMDLINE_BAD_ENDING 1\n #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2\n static const char *split_cmdline_errors[] = {\ndiff --git a/alias.h b/alias.h\nindex fbf1d22a94..79933f2457 100644\n--- a/alias.h\n+++ b/alias.h\n@@ -1,9 +1,12 @@\n #ifndef __ALIAS_H__\n #define __ALIAS_H__\n \n+struct string_list;\n+\n char *alias_lookup(const char *alias);\n int split_cmdline(char *cmdline, const char ***argv);\n /* Takes a negative value returned by split_cmdline */\n const char *split_cmdline_strerror(int cmdline_errno);\n+void list_aliases(struct string_list *list);\n \n #endif\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 217c8a3d3b..98f278fb9a 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -834,51 +834,11 @@ __git_complete_strategy ()\n \treturn 1\n }\n \n-# __git_commands requires 1 argument:\n-# 1: the command group, either \"all\" or \"porcelain\"\n-__git_commands () {\n-\tcase \"$1\" in\n-\tporcelain)\n-\t\tif test -n \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n-\t\tthen\n-\t\t\tprintf \"%s\" \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n-\t\telse\n-\t\t\tgit --list-cmds=list-mainporcelain,others,nohelpers,list-complete\n-\t\tfi\n-\t\t;;\n-\tall)\n-\t\tif test -n \"$GIT_TESTING_ALL_COMMAND_LIST\"\n-\t\tthen\n-\t\t\tprintf \"%s\" \"$GIT_TESTING_ALL_COMMAND_LIST\"\n-\t\telse\n-\t\t\tgit --list-cmds=main,others,nohelpers\n-\t\tfi\n-\t\t;;\n-\tesac\n-}\n-\n-__git_list_all_commands ()\n-{\n-\t__git_commands all\n-}\n-\n __git_all_commands=\n __git_compute_all_commands ()\n {\n \ttest -n \"$__git_all_commands\" ||\n-\t__git_all_commands=$(__git_list_all_commands)\n-}\n-\n-__git_list_porcelain_commands ()\n-{\n-\t__git_commands porcelain\n-}\n-\n-__git_porcelain_commands=\n-__git_compute_porcelain_commands ()\n-{\n-\ttest -n \"$__git_porcelain_commands\" ||\n-\t__git_porcelain_commands=$(__git_list_porcelain_commands)\n+\t__git_all_commands=$(git --list-cmds=main,others,alias,nohelpers)\n }\n \n # Lists all set config variables starting with the given section prefix,\n@@ -896,11 +856,6 @@ __git_pretty_aliases ()\n \t__git_get_config_variables \"pretty\"\n }\n \n-__git_aliases ()\n-{\n-\t__git_get_config_variables \"alias\"\n-}\n-\n # __git_aliased_command requires 1 argument\n __git_aliased_command ()\n {\n@@ -1500,13 +1455,6 @@ _git_grep ()\n \t__git_complete_refs\n }\n \n-__git_all_guides=\n-__git_compute_all_guides ()\n-{\n-\ttest -n \"$__git_all_guides\" ||\n-\t__git_all_guides=$(git --list-cmds=list-guide)\n-}\n-\n _git_help ()\n {\n \tcase \"$cur\" in\n@@ -1515,11 +1463,12 @@ _git_help ()\n \t\treturn\n \t\t;;\n \tesac\n-\t__git_compute_all_commands\n-\t__git_compute_all_guides\n-\t__gitcomp \"$__git_all_commands $(__git_aliases) $__git_all_guides\n-\t\tgitk\n-\t\t\"\n+\tif test -n \"$GIT_TESTING_ALL_COMMAND_LIST\"\n+\tthen\n+\t\t__gitcomp \"$GIT_TESTING_ALL_COMMAND_LIST $(git --list-cmds=alias,list-guide) gitk\"\n+\telse\n+\t\t__gitcomp \"$(git --list-cmds=main,nohelpers,alias,list-guide) gitk\"\n+\tfi\n }\n \n _git_init ()\n@@ -3058,8 +3007,14 @@ __git_main ()\n \t\t\t--help\n \t\t\t\"\n \t\t\t;;\n-\t\t*)     __git_compute_porcelain_commands\n-\t\t       __gitcomp \"$__git_porcelain_commands $(__git_aliases)\" ;;\n+\t\t*)\n+\t\t\tif test -n \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n+\t\t\tthen\n+\t\t\t\t__gitcomp \"$GIT_TESTING_PORCELAIN_COMMAND_LIST\"\n+\t\t\telse\n+\t\t\t\t__gitcomp \"$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete)\"\n+\t\t\tfi\n+\t\t\t;;\n \t\tesac\n \t\treturn\n \tfi\ndiff --git a/git.c b/git.c\nindex f6ae79ffaf..63acd9ea81 100644\n--- a/git.c\n+++ b/git.c\n@@ -75,6 +75,8 @@ static int list_cmds(const char *spec)\n \t\t\tlist_all_other_cmds(&list);\n \t\telse if (match_token(spec, len, \"nohelpers\"))\n \t\t\texclude_helpers_from_list(&list);\n+\t\telse if (match_token(spec, len, \"alias\"))\n+\t\t\tlist_aliases(&list);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 2f16679380..5863b1acac 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -1192,17 +1192,6 @@ test_expect_success '__git_pretty_aliases' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success '__git_aliases' '\n-\tcat >expect <<-EOF &&\n-\tci\n-\tco\n-\tEOF\n-\ttest_config alias.ci commit &&\n-\ttest_config alias.co checkout &&\n-\t__git_aliases >actual &&\n-\ttest_cmp expect actual\n-'\n-\n test_expect_success 'basic' '\n \trun_completion \"git \" &&\n \t# built-in\n@@ -1511,13 +1500,6 @@ test_expect_success 'sourcing the completion script clears cached commands' '\n \tverbose test -z \"$__git_all_commands\"\n '\n \n-test_expect_success 'sourcing the completion script clears cached porcelain commands' '\n-\t__git_compute_porcelain_commands &&\n-\tverbose test -n \"$__git_porcelain_commands\" &&\n-\t. \"$GIT_BUILD_DIR/contrib/completion/git-completion.bash\" &&\n-\tverbose test -z \"$__git_porcelain_commands\"\n-'\n-\n test_expect_success !GETTEXT_POISON 'sourcing the completion script clears cached merge strategies' '\n \t__git_compute_merge_strategies &&\n \tverbose test -n \"$__git_merge_strategies\" &&\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348124","messageId":"20180520184009.976-6-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 05/17] git.c: convert --list-* to --list-cmds=*","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:39:57Z","receivedAt":"2018-05-20T18:41:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Even if these are hidden options, let's make them a bit more generic\nsince we're introducing more listing types shortly. The code is\nstructured to allow combining multiple listing types together because\nwe will soon add more types the 'builtins'.\n\n'parseopt' remains separate because it has separate (SPC) to match\ngit-completion.bash needs and will not combine with others.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git.txt                  |  6 +++++\n contrib/completion/git-completion.bash |  2 +-\n git.c                                  | 37 +++++++++++++++++++++-----\n t/t0012-help.sh                        |  2 +-\n 4 files changed, 39 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 4767860e72..2800e3d188 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -163,6 +163,12 @@ foo.bar= ...`) sets `foo.bar` to the empty string which `git config\n \tDo not perform optional operations that require locks. This is\n \tequivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.\n \n+--list-cmds=group[,group...]::\n+\tList commands by group. This is an internal/experimental\n+\toption and may change or be removed in the future. Supported\n+\tgroups are: builtins, parseopt (builtin commands that use\n+\tparse-options).\n+\n GIT COMMANDS\n ------------\n \ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex a757073945..3556838759 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -3049,7 +3049,7 @@ __git_complete_common () {\n __git_cmds_with_parseopt_helper=\n __git_support_parseopt_helper () {\n \ttest -n \"$__git_cmds_with_parseopt_helper\" ||\n-\t\t__git_cmds_with_parseopt_helper=\"$(__git --list-parseopt-builtins)\"\n+\t\t__git_cmds_with_parseopt_helper=\"$(__git --list-cmds=parseopt)\"\n \n \tcase \" $__git_cmds_with_parseopt_helper \" in\n \t*\" $1 \"*)\ndiff --git a/git.c b/git.c\nindex 3a89893712..cd85355d81 100644\n--- a/git.c\n+++ b/git.c\n@@ -38,6 +38,30 @@ static int use_pager = -1;\n \n static void list_builtins(unsigned int exclude_option, char sep);\n \n+static int match_token(const char *spec, int len, const char *token)\n+{\n+\tint token_len = strlen(token);\n+\n+\treturn len == token_len && !strncmp(spec, token, token_len);\n+}\n+\n+static int list_cmds(const char *spec)\n+{\n+\twhile (*spec) {\n+\t\tconst char *sep = strchrnul(spec, ',');\n+\t\tint len = sep - spec;\n+\n+\t\tif (match_token(spec, len, \"builtins\"))\n+\t\t\tlist_builtins(0, '\\n');\n+\t\telse\n+\t\t\tdie(_(\"unsupported command listing type '%s'\"), spec);\n+\t\tspec += len;\n+\t\tif (*spec == ',')\n+\t\t\tspec++;\n+\t}\n+\treturn 0;\n+}\n+\n static void commit_pager_choice(void) {\n \tswitch (use_pager) {\n \tcase 0:\n@@ -223,12 +247,13 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\t}\n \t\t\t(*argv)++;\n \t\t\t(*argc)--;\n-\t\t} else if (!strcmp(cmd, \"--list-builtins\")) {\n-\t\t\tlist_builtins(0, '\\n');\n-\t\t\texit(0);\n-\t\t} else if (!strcmp(cmd, \"--list-parseopt-builtins\")) {\n-\t\t\tlist_builtins(NO_PARSEOPT, ' ');\n-\t\t\texit(0);\n+\t\t} else if (skip_prefix(cmd, \"--list-cmds=\", &cmd)) {\n+\t\t\tif (!strcmp(cmd, \"parseopt\")) {\n+\t\t\t\tlist_builtins(NO_PARSEOPT, ' ');\n+\t\t\t\texit(0);\n+\t\t\t} else {\n+\t\t\t\texit(list_cmds(cmd));\n+\t\t\t}\n \t\t} else {\n \t\t\tfprintf(stderr, _(\"unknown option: %s\\n\"), cmd);\n \t\t\tusage(git_usage_string);\ndiff --git a/t/t0012-help.sh b/t/t0012-help.sh\nindex c096f33505..3c91a9024a 100755\n--- a/t/t0012-help.sh\n+++ b/t/t0012-help.sh\n@@ -59,7 +59,7 @@ test_expect_success 'git help' '\n '\n \n test_expect_success 'generate builtin list' '\n-\tgit --list-builtins >builtins\n+\tgit --list-cmds=builtins >builtins\n '\n \n while read builtin\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348125","messageId":"20180520184009.976-5-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 04/17] Remove common-cmds.h","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:39:56Z","receivedAt":"2018-05-20T18:41:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"After the last patch, common-cmds.h is no longer used (and it was\nactually broken). Remove all related code. command-list.h will take\nits place from now on.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore          |  1 -\n Makefile            | 17 ++++++-----------\n generate-cmdlist.sh | 46 +++------------------------------------------\n 3 files changed, 9 insertions(+), 55 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex d4c3914167..0836083992 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -179,7 +179,6 @@\n /gitweb/gitweb.cgi\n /gitweb/static/gitweb.js\n /gitweb/static/gitweb.min.*\n-/common-cmds.h\n /command-list.h\n *.tar.gz\n *.dsc\ndiff --git a/Makefile b/Makefile\nindex 5c58b0b692..a60a78ee67 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -757,7 +757,7 @@ LIB_FILE = libgit.a\n XDIFF_LIB = xdiff/lib.a\n VCSSVN_LIB = vcs-svn/lib.a\n \n-GENERATED_H += common-cmds.h command-list.h\n+GENERATED_H += command-list.h\n \n LIB_H = $(shell $(FIND) . \\\n \t-name .git -prune -o \\\n@@ -1914,9 +1914,9 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \\\n \t\t$(filter %.o,$^) $(LIBS)\n \n-help.sp help.s help.o: common-cmds.h command-list.h\n+help.sp help.s help.o: command-list.h\n \n-builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h command-list.h GIT-PREFIX\n+builtin/help.sp builtin/help.s builtin/help.o: command-list.h GIT-PREFIX\n builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \\\n \t'-DGIT_HTML_PATH=\"$(htmldir_relative_SQ)\"' \\\n \t'-DGIT_MAN_PATH=\"$(mandir_relative_SQ)\"' \\\n@@ -1935,11 +1935,6 @@ $(BUILT_INS): git$X\n \tln -s $< $@ 2>/dev/null || \\\n \tcp $< $@\n \n-common-cmds.h: generate-cmdlist.sh command-list.txt\n-\n-common-cmds.h: $(wildcard Documentation/git-*.txt)\n-\t$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt COMMON >$@+ && mv $@+ $@\n-\n command-list.h: generate-cmdlist.sh command-list.txt\n \n command-list.h: $(wildcard Documentation/git-*.txt)\n@@ -2153,7 +2148,7 @@ else\n # Dependencies on header files, for platforms that do not support\n # the gcc -MMD option.\n #\n-# Dependencies on automatically generated headers such as common-cmds.h or command-list.h\n+# Dependencies on automatically generated headers such as command-list.h\n # should _not_ be included here, since they are necessary even when\n # building an object for the first time.\n \n@@ -2532,7 +2527,7 @@ sparse: $(SP_OBJ)\n style:\n \tgit clang-format --style file --diff --extensions c,h\n \n-check: common-cmds.h command-list.h\n+check: command-list.h\n \t@if sparse; \\\n \tthen \\\n \t\techo >&2 \"Use 'make sparse' instead\"; \\\n@@ -2780,7 +2775,7 @@ clean: profile-clean coverage-clean\n \t$(RM) $(TEST_PROGRAMS) $(NO_INSTALL)\n \t$(RM) -r bin-wrappers $(dep_dirs)\n \t$(RM) -r po/build/\n-\t$(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h command-list.h $(ETAGS_TARGET) tags cscope*\n+\t$(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*\n \t$(RM) -r $(GIT_TARNAME) .doc-tmp-dir\n \t$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex 9eb22c4ef1..3bcc1ee57d 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -68,46 +68,6 @@ struct cmdname_help {\n \tuint32_t category;\n };\n \"\n-if test -z \"$2\"\n-then\n-\tdefine_categories \"$1\"\n-\techo\n-\tprint_command_list \"$1\"\n-\texit 0\n-fi\n-\n-echo \"static const char *common_cmd_groups[] = {\"\n-\n-grps=grps$$.tmp\n-match=match$$.tmp\n-trap \"rm -f '$grps' '$match'\" 0 1 2 3 15\n-\n-sed -n '\n-\t1,/^### common groups/b\n-\t/^### command list/q\n-\t/^#/b\n-\t/^[ \t]*$/b\n-\th;s/^[^ \t][^ \t]*[ \t][ \t]*\\(.*\\)/\tN_(\"\\1\"),/p\n-\tg;s/^\\([^ \t][^ \t]*\\)[ \t].*/\\1/w '$grps'\n-\t' \"$1\"\n-printf '};\\n\\n'\n-\n-n=0\n-substnum=\n-while read grp\n-do\n-\techo \"^git-..*[ \t]$grp\"\n-\tsubstnum=\"$substnum${substnum:+;}s/[ \t]$grp/$n/\"\n-\tn=$(($n+1))\n-done <\"$grps\" >\"$match\"\n-\n-printf 'static struct cmdname_help common_cmds[] = {\\n'\n-grep -f \"$match\" \"$1\" |\n-sed 's/^git-//' |\n-sort |\n-while read cmd tags\n-do\n-\ttag=$(echo \"$tags\" | sed \"$substnum; s/[^0-9]//g\")\n-\techo \"\t{\\\"$cmd\\\", $(get_synopsis git-$cmd), $tag},\"\n-done\n-echo \"};\"\n+define_categories \"$1\"\n+echo\n+print_command_list \"$1\"\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348126","messageId":"20180520184009.976-10-pclouds@gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-1-pclouds@gmail.com","subject":"[PATCH v2 09/17] help: add \"-a --verbose\" to list all commands with synopsis","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-20T18:40:01Z","receivedAt":"2018-05-20T18:41:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This lists all recognized commands [1] by category. The group order\nfollows closely git.txt.\n\n[1] We may actually show commands that are not built (e.g. if you set\nNO_PERL you don't have git-instaweb but it's still listed here). I\nignore the problem because on Linux a git package could be split\nanyway. The \"git-core\" package may not contain git-instaweb even if\nit's built because it may end up in a separate package. We can't know\nanyway.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-help.txt |  4 +++-\n builtin/help.c             |  7 +++++++\n help.c                     | 16 ++++++++++++++++\n help.h                     |  2 ++\n t/t0012-help.sh            |  9 +++++++++\n 5 files changed, 37 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-help.txt b/Documentation/git-help.txt\nindex 40d328a4b3..a40fc38d8b 100644\n--- a/Documentation/git-help.txt\n+++ b/Documentation/git-help.txt\n@@ -8,7 +8,7 @@ git-help - Display help information about Git\n SYNOPSIS\n --------\n [verse]\n-'git help' [-a|--all] [-g|--guide]\n+'git help' [-a|--all [--verbose]] [-g|--guide]\n \t   [-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]\n \n DESCRIPTION\n@@ -42,6 +42,8 @@ OPTIONS\n --all::\n \tPrints all the available commands on the standard output. This\n \toption overrides any given command or guide name.\n+\tWhen used with `--verbose` print description for all recognized\n+\tcommands.\n \n -g::\n --guides::\ndiff --git a/builtin/help.c b/builtin/help.c\nindex 598867cfea..0e0af8426a 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -36,6 +36,7 @@ static const char *html_path;\n \n static int show_all = 0;\n static int show_guides = 0;\n+static int verbose;\n static unsigned int colopts;\n static enum help_format help_format = HELP_FORMAT_NONE;\n static int exclude_guides;\n@@ -48,6 +49,7 @@ static struct option builtin_help_options[] = {\n \t\t\tHELP_FORMAT_WEB),\n \tOPT_SET_INT('i', \"info\", &help_format, N_(\"show info page\"),\n \t\t\tHELP_FORMAT_INFO),\n+\tOPT__VERBOSE(&verbose, N_(\"print command description\")),\n \tOPT_END(),\n };\n \n@@ -463,6 +465,11 @@ int cmd_help(int argc, const char **argv, const char *prefix)\n \n \tif (show_all) {\n \t\tgit_config(git_help_config, NULL);\n+\t\tif (verbose) {\n+\t\t\tsetup_pager();\n+\t\t\tlist_all_cmds_help();\n+\t\t\treturn 0;\n+\t\t}\n \t\tprintf(_(\"usage: %s%s\"), _(git_usage_string), \"\\n\\n\");\n \t\tload_command_list(\"git-\", &main_cmds, &other_cmds);\n \t\tlist_commands(colopts, &main_cmds, &other_cmds);\ndiff --git a/help.c b/help.c\nindex 1117f7d1d1..c7df1d2338 100644\n--- a/help.c\n+++ b/help.c\n@@ -27,6 +27,17 @@ static struct category_description common_categories[] = {\n \t{ CAT_remote, N_(\"collaborate (see also: git help workflows)\") },\n \t{ 0, NULL }\n };\n+static struct category_description main_categories[] = {\n+\t{ CAT_mainporcelain, N_(\"Main Porcelain Commands\") },\n+\t{ CAT_ancillarymanipulators, N_(\"Ancillary Commands / Manipulators\") },\n+\t{ CAT_ancillaryinterrogators, N_(\"Ancillary Commands / Interrogators\") },\n+\t{ CAT_foreignscminterface, N_(\"Interacting with Others\") },\n+\t{ CAT_plumbingmanipulators, N_(\"Low-level Commands / Manipulators\") },\n+\t{ CAT_plumbinginterrogators, N_(\"Low-level Commands / Interrogators\") },\n+\t{ CAT_synchingrepositories, N_(\"Low-level Commands / Synching Repositories\") },\n+\t{ CAT_purehelpers, N_(\"Low-level Commands / Internal Helpers\") },\n+\t{ 0, NULL }\n+};\n \n static const char *drop_prefix(const char *name)\n {\n@@ -352,6 +363,11 @@ void list_cmds_by_category(struct string_list *list,\n \t}\n }\n \n+void list_all_cmds_help(void)\n+{\n+\tprint_cmd_by_category(main_categories);\n+}\n+\n int is_in_cmdlist(struct cmdnames *c, const char *s)\n {\n \tint i;\ndiff --git a/help.h b/help.h\nindex 734bba32d3..40917fc38c 100644\n--- a/help.h\n+++ b/help.h\n@@ -19,6 +19,8 @@ static inline void mput_char(char c, unsigned int num)\n }\n \n extern void list_common_cmds_help(void);\n+extern void list_all_cmds_help(void);\n+\n extern void list_all_main_cmds(struct string_list *list);\n extern void list_all_other_cmds(struct string_list *list);\n extern void list_cmds_by_category(struct string_list *list,\ndiff --git a/t/t0012-help.sh b/t/t0012-help.sh\nindex 3c91a9024a..060df24c2d 100755\n--- a/t/t0012-help.sh\n+++ b/t/t0012-help.sh\n@@ -25,6 +25,15 @@ test_expect_success \"setup\" '\n \tEOF\n '\n \n+# make sure to exercise these code paths, the output is a bit tricky\n+# to verify\n+test_expect_success 'basic help commands' '\n+\tgit help >/dev/null &&\n+\tgit help -a >/dev/null &&\n+\tgit help -g >/dev/null &&\n+\tgit help -av >/dev/null\n+'\n+\n test_expect_success \"works for commands and guides by default\" '\n \tconfigure_help &&\n \tgit help status &&\n-- \n2.17.0.705.g3525833791\n\n"},{"id":"348142","messageId":"xmqqin7h3jhq.fsf@gitster-ct.c.googlers.com","threadId":"48522","inReplyTo":"20180519042752.8666-14-pclouds@gmail.com","subject":"Re: [PATCH 13/14] completion: reduce completable command list","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-21T01:06:25Z","receivedAt":"2018-05-21T01:06:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> The following commands are removed from the complete list:\n>\n> - interpreter-trailers not for interactive use\n\nTypo here.  see below.\n\n> -git-interpret-trailers                  purehelpers                     complete\n> +git-interpret-trailers                  purehelpers\n"},{"id":"363764","messageId":"87pnuzzgpi.fsf@evledraar.gmail.com","threadId":"48522","inReplyTo":"20180520184009.976-11-pclouds@gmail.com","subject":"Re: [PATCH v2 10/17] help: use command-list.txt for the source of guides","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-11-20T19:34:01Z","receivedAt":"2018-11-20T19:34:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, May 20 2018, Nguyễn Thái Ngọc Duy wrote:\n\n> The help command currently hard codes the list of guides and their\n> summary in C. Let's move this list to command-list.txt. This lets us\n> extract summary lines from Documentation/git*.txt. This also\n> potentially lets us list guides in git.txt, but I'll leave that for\n> now.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/gitattributes.txt        |  2 +-\n>  Documentation/gitmodules.txt           |  2 +-\n>  Documentation/gitrevisions.txt         |  2 +-\n>  Makefile                               |  2 +-\n>  builtin/help.c                         | 32 --------------------------\n>  command-list.txt                       | 16 +++++++++++++\n>  contrib/completion/git-completion.bash | 15 ++++++++----\n>  help.c                                 | 21 +++++++++++++----\n>  help.h                                 |  1 +\n>  t/t0012-help.sh                        |  6 +++++\n>  10 files changed, 54 insertions(+), 45 deletions(-)\n>\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> index 1094fe2b5b..083c2f380d 100644\n> --- a/Documentation/gitattributes.txt\n> +++ b/Documentation/gitattributes.txt\n> @@ -3,7 +3,7 @@ gitattributes(5)\n>\n>  NAME\n>  ----\n> -gitattributes - defining attributes per path\n> +gitattributes - Defining attributes per path\n>\n>  SYNOPSIS\n>  --------\n> diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt\n> index db5d47eb19..4d63def206 100644\n> --- a/Documentation/gitmodules.txt\n> +++ b/Documentation/gitmodules.txt\n> @@ -3,7 +3,7 @@ gitmodules(5)\n>\n>  NAME\n>  ----\n> -gitmodules - defining submodule properties\n> +gitmodules - Defining submodule properties\n>\n>  SYNOPSIS\n>  --------\n> diff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt\n> index 27dec5b91d..1f6cceaefb 100644\n> --- a/Documentation/gitrevisions.txt\n> +++ b/Documentation/gitrevisions.txt\n> @@ -3,7 +3,7 @@ gitrevisions(7)\n>\n>  NAME\n>  ----\n> -gitrevisions - specifying revisions and ranges for Git\n> +gitrevisions - Specifying revisions and ranges for Git\n>\n>  SYNOPSIS\n>  --------\n> diff --git a/Makefile b/Makefile\n> index a60a78ee67..1efb751e46 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -1937,7 +1937,7 @@ $(BUILT_INS): git$X\n>\n>  command-list.h: generate-cmdlist.sh command-list.txt\n>\n> -command-list.h: $(wildcard Documentation/git-*.txt)\n> +command-list.h: $(wildcard Documentation/git*.txt)\n>  \t$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@\n>\n>  SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\\\n> diff --git a/builtin/help.c b/builtin/help.c\n> index 0e0af8426a..5727fb5e51 100644\n> --- a/builtin/help.c\n> +++ b/builtin/help.c\n> @@ -402,38 +402,6 @@ static void show_html_page(const char *git_cmd)\n>  \topen_html(page_path.buf);\n>  }\n>\n> -static struct {\n> -\tconst char *name;\n> -\tconst char *help;\n> -} common_guides[] = {\n> -\t{ \"attributes\", N_(\"Defining attributes per path\") },\n> -\t{ \"everyday\", N_(\"Everyday Git With 20 Commands Or So\") },\n> -\t{ \"glossary\", N_(\"A Git glossary\") },\n> -\t{ \"ignore\", N_(\"Specifies intentionally untracked files to ignore\") },\n> -\t{ \"modules\", N_(\"Defining submodule properties\") },\n> -\t{ \"revisions\", N_(\"Specifying revisions and ranges for Git\") },\n> -\t{ \"tutorial\", N_(\"A tutorial introduction to Git (for version 1.5.1 or newer)\") },\n> -\t{ \"workflows\", N_(\"An overview of recommended workflows with Git\") },\n> -};\n> -\n> -static void list_common_guides_help(void)\n> -{\n> -\tint i, longest = 0;\n> -\n> -\tfor (i = 0; i < ARRAY_SIZE(common_guides); i++) {\n> -\t\tif (longest < strlen(common_guides[i].name))\n> -\t\t\tlongest = strlen(common_guides[i].name);\n> -\t}\n> -\n> -\tputs(_(\"The common Git guides are:\\n\"));\n> -\tfor (i = 0; i < ARRAY_SIZE(common_guides); i++) {\n> -\t\tprintf(\"   %s   \", common_guides[i].name);\n> -\t\tmput_char(' ', longest - strlen(common_guides[i].name));\n> -\t\tputs(_(common_guides[i].help));\n> -\t}\n> -\tputchar('\\n');\n> -}\n> -\n>  static const char *check_git_cmd(const char* cmd)\n>  {\n>  \tchar *alias;\n> diff --git a/command-list.txt b/command-list.txt\n> index 3bd23201a6..99ddc231c1 100644\n> --- a/command-list.txt\n> +++ b/command-list.txt\n> @@ -139,3 +139,19 @@ gitweb                                  ancillaryinterrogators\n>  git-whatchanged                         ancillaryinterrogators\n>  git-worktree                            mainporcelain\n>  git-write-tree                          plumbingmanipulators\n> +gitattributes                           guide\n> +gitcli                                  guide\n> +gitcore-tutorial                        guide\n> +gitcvs-migration                        guide\n> +gitdiffcore                             guide\n> +giteveryday                             guide\n> +gitglossary                             guide\n> +githooks                                guide\n> +gitignore                               guide\n> +gitmodules                              guide\n> +gitnamespaces                           guide\n> +gitrepository-layout                    guide\n> +gitrevisions                            guide\n> +gittutorial-2                           guide\n> +gittutorial                             guide\n> +gitworkflows                            guide\n> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\n> index 62ca8641f4..4e724a5b76 100644\n> --- a/contrib/completion/git-completion.bash\n> +++ b/contrib/completion/git-completion.bash\n> @@ -1575,6 +1575,13 @@ _git_grep ()\n>  \t__git_complete_refs\n>  }\n>\n> +__git_all_guides=\n> +__git_compute_all_guides ()\n> +{\n> +\ttest -n \"$__git_all_guides\" ||\n> +\t__git_all_guides=$(git --list-cmds=list-guide)\n> +}\n> +\n>  _git_help ()\n>  {\n>  \tcase \"$cur\" in\n> @@ -1584,11 +1591,9 @@ _git_help ()\n>  \t\t;;\n>  \tesac\n>  \t__git_compute_all_commands\n> -\t__gitcomp \"$__git_all_commands $(__git_aliases)\n> -\t\tattributes cli core-tutorial cvs-migration\n> -\t\tdiffcore everyday gitk glossary hooks ignore modules\n> -\t\tnamespaces repository-layout revisions tutorial tutorial-2\n> -\t\tworkflows\n> +\t__git_compute_all_guides\n> +\t__gitcomp \"$__git_all_commands $(__git_aliases) $__git_all_guides\n> +\t\tgitk\n>  \t\t\"\n>  }\n>\n> diff --git a/help.c b/help.c\n> index c7df1d2338..23924dd300 100644\n> --- a/help.c\n> +++ b/help.c\n> @@ -39,12 +39,14 @@ static struct category_description main_categories[] = {\n>  \t{ 0, NULL }\n>  };\n>\n> -static const char *drop_prefix(const char *name)\n> +static const char *drop_prefix(const char *name, uint32_t category)\n>  {\n>  \tconst char *new_name;\n>\n>  \tif (skip_prefix(name, \"git-\", &new_name))\n>  \t\treturn new_name;\n> +\tif (category == CAT_guide && skip_prefix(name, \"git\", &new_name))\n> +\t\treturn new_name;\n>  \treturn name;\n>\n>  }\n> @@ -66,7 +68,7 @@ static void extract_cmds(struct cmdname_help **p_cmds, uint32_t mask)\n>  \t\t\tcontinue;\n>\n>  \t\tcmds[nr] = *cmd;\n> -\t\tcmds[nr].name = drop_prefix(cmd->name);\n> +\t\tcmds[nr].name = drop_prefix(cmd->name, cmd->category);\n>\n>  \t\tnr++;\n>  \t}\n> @@ -358,11 +360,22 @@ void list_cmds_by_category(struct string_list *list,\n>  \tfor (i = 0; i < n; i++) {\n>  \t\tstruct cmdname_help *cmd = command_list + i;\n>\n> -\t\tif (cmd->category & cat_id)\n> -\t\t\tstring_list_append(list, drop_prefix(cmd->name));\n> +\t\tif (!(cmd->category & cat_id))\n> +\t\t\tcontinue;\n> +\t\tstring_list_append(list, drop_prefix(cmd->name, cmd->category));\n>  \t}\n>  }\n>\n> +void list_common_guides_help(void)\n> +{\n> +\tstruct category_description catdesc[] = {\n> +\t\t{ CAT_guide, N_(\"The common Git guides are:\") },\n> +\t\t{ 0, NULL }\n> +\t};\n> +\tprint_cmd_by_category(catdesc);\n> +\tputchar('\\n');\n> +}\n> +\n>  void list_all_cmds_help(void)\n>  {\n>  \tprint_cmd_by_category(main_categories);\n> diff --git a/help.h b/help.h\n> index 40917fc38c..b2293e99be 100644\n> --- a/help.h\n> +++ b/help.h\n> @@ -20,6 +20,7 @@ static inline void mput_char(char c, unsigned int num)\n>\n>  extern void list_common_cmds_help(void);\n>  extern void list_all_cmds_help(void);\n> +extern void list_common_guides_help(void);\n>\n>  extern void list_all_main_cmds(struct string_list *list);\n>  extern void list_all_other_cmds(struct string_list *list);\n> diff --git a/t/t0012-help.sh b/t/t0012-help.sh\n> index 060df24c2d..bc27df7f38 100755\n> --- a/t/t0012-help.sh\n> +++ b/t/t0012-help.sh\n> @@ -66,6 +66,12 @@ test_expect_success 'git help' '\n>  \ttest_i18ngrep \"^   commit \" help.output &&\n>  \ttest_i18ngrep \"^   fetch  \" help.output\n>  '\n> +test_expect_success 'git help -g' '\n> +\tgit help -g >help.output &&\n> +\ttest_i18ngrep \"^   attributes \" help.output &&\n> +\ttest_i18ngrep \"^   everyday   \" help.output &&\n> +\ttest_i18ngrep \"^   tutorial   \" help.output\n> +'\n>\n>  test_expect_success 'generate builtin list' '\n>  \tgit --list-cmds=builtins >builtins\n\nOn a branch I branched off current master (bb75be6cb9) I was\nconsistently getting failures on this test:\n\n    expecting success:\n            git help -g >help.output &&\n            test_i18ngrep \"^   attributes \" help.output &&\n            test_i18ngrep \"^   everyday   \" help.output &&\n            test_i18ngrep \"^   tutorial   \" help.output\n\n       attributes      Defining attributes per path\n       everyday        A useful minimum set of commands for Everyday Git\n    error: 'grep ^   tutorial    help.output' didn't find a match in:\n\n    The common Git guides are:\n       attributes      Defining attributes per path\n       cli             Git command-line interface and conventions\n       core-tutorial   A Git core tutorial for developers\n       cvs-migration   Git for CVS users\n       diffcore        Tweaking diff output\n       everyday        A useful minimum set of commands for Everyday Git\n       glossary        A Git Glossary\n       hooks           Hooks used by Git\n       ignore          Specifies intentionally untracked files to ignore\n       modules         Defining submodule properties\n\n    'git help -a' and 'git help -g' list available subcommands and some\n    concept guides. See 'git help <command>' or 'git help <concept>'\n    to read about a specific subcommand or concept.\n    not ok 7 - git help -g\n    #\n    #               git help -g >help.output &&\n    #               test_i18ngrep \"^   attributes \" help.output &&\n    #               test_i18ngrep \"^   everyday   \" help.output &&\n    #               test_i18ngrep \"^   tutorial   \" help.output\n    #\n\nThis was after running \"make all doc\". The issue went away when I ran\n'git clean -dxf' and I haven't been able to reproduce it again. Just\nwanted to leave a note in case anyone else runs into this heisenbug,\nperhaps there's some missing dependency in the command-list.h generation\nlogic.\n"}]}