+struct category_description {
+ uint32_t category;
+ const char *desc;
+};
+static uint32_t common_mask =
+ CAT_init | CAT_worktree | CAT_info |
+ CAT_history | CAT_remote;
+static struct category_description common_categories[] = {
+ { CAT_init, N_("start a working area (see also: git help tutorial)") },
+ { CAT_worktree, N_("work on the current change (see also: git help everyday)") },
+ { CAT_info, N_("examine the history and state (see also: git help revisions)") },
+ { CAT_history, N_("grow, mark and tweak your common history") },
+ { CAT_remote, N_("collaborate (see also: git help workflows)") },
+ { 0, NULL }
+};
+static struct category_description main_categories[] = {
+ { CAT_mainporcelain, N_("Main Porcelain Commands") },
+ { CAT_ancillarymanipulators, N_("Ancillary Commands / Manipulators") },
+ { CAT_ancillaryinterrogators, N_("Ancillary Commands / Interrogators") },
+ { CAT_foreignscminterface, N_("Interacting with Others") },
+ { CAT_plumbingmanipulators, N_("Low-level Commands / Manipulators") },
+ { CAT_plumbinginterrogators, N_("Low-level Commands / Interrogators") },
+ { CAT_synchingrepositories, N_("Low-level Commands / Syncing Repositories") },
+ { CAT_purehelpers, N_("Low-level Commands / Internal Helpers") },
+ { 0, NULL }
+};
+
+static const char *drop_prefix(const char *name, uint32_t category)
+{
+ const char *new_name;
+
+ if (skip_prefix(name, "git-", &new_name))
+ return new_name;
+ if (category == CAT_guide && skip_prefix(name, "git", &new_name))
+ return new_name;
+ return name;
+
+}
+
+static void extract_cmds(struct cmdname_help **p_cmds, uint32_t mask)
+{
+ int i, nr = 0;
+ struct cmdname_help *cmds;
+
+ if (ARRAY_SIZE(command_list) == 0)
+ BUG("empty command_list[] is a sign of broken generate-cmdlist.sh");
+
+ ALLOC_ARRAY(cmds, ARRAY_SIZE(command_list) + 1);
+
+ for (i = 0; i < ARRAY_SIZE(command_list); i++) {
+ const struct cmdname_help *cmd = command_list + i;
+
+ if (!(cmd->category & mask))
+ continue;
+
+ cmds[nr] = *cmd;
+ cmds[nr].name = drop_prefix(cmd->name, cmd->category);
+
+ nr++;
+ }
+ cmds[nr].name = NULL;
+ *p_cmds = cmds;
+}
+
+static void print_command_list(const struct cmdname_help *cmds,
+ uint32_t mask, int longest)
+{
+ int i;
+
+ for (i = 0; cmds[i].name; i++) {
+ if (cmds[i].category & mask) {
+ size_t len = strlen(cmds[i].name);
+ printf(" %s ", cmds[i].name);
+ if (longest > len)
+ mput_char(' ', longest - len);
+ puts(_(cmds[i].help));
+ }
+ }
+}
+
+static int cmd_name_cmp(const void *elem1, const void *elem2)
+{
+ const struct cmdname_help *e1 = elem1;
+ const struct cmdname_help *e2 = elem2;
+
+ return strcmp(e1->name, e2->name);
+}
+
+static void print_cmd_by_category(const struct category_description *catdesc,
+ int *longest_p)
+{
+ struct cmdname_help *cmds;
+ int longest = 0;
+ int i, nr = 0;
+ uint32_t mask = 0;
+
+ for (i = 0; catdesc[i].desc; i++)
+ mask |= catdesc[i].category;
+
+ extract_cmds(&cmds, mask);
+
+ for (i = 0; cmds[i].name; i++, nr++) {
+ if (longest < strlen(cmds[i].name))
+ longest = strlen(cmds[i].name);
+ }
+ QSORT(cmds, nr, cmd_name_cmp);
+
+ for (i = 0; catdesc[i].desc; i++) {
+ uint32_t mask = catdesc[i].category;
+ const char *desc = catdesc[i].desc;
+
+ printf("\n%s\n", _(desc));
+ print_command_list(cmds, mask, longest);
+ }
+ free(cmds);
+ if (longest_p)
+ *longest_p = longest;
+}
+