git: add `deprecated` category to --list-cmds

With 145 builtin commands (according to `git --list-cmds=builtins`), users are probably not keeping on top of which ones (if any) are deprecated. Let’s expand the experimental `--list-cmds`[1] to allow users and programs to query for this information. We will also use this in an upcoming commit to implement `is_deprecated_command`. [1]: Using something which is experimental to query for deprecations is perhaps not the most ideal approach, but it is simple to implement and better than having to scan the documentation Acked-by: Patrick Steinhardt <ps@pks.im> Helped-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Kristoffer Haugsbakk committed Sep 17, 2025 at 22:24 UTC 5f31632ed7d8c2928b5cdd7a1358367415d24535
2 files changed +20 -9
Documentation/git.adoc
+2 -1
@@ -219,7 +219,8 @@ If you just want to run git as if it was started in `<path>` then use
219 List commands by group. This is an internal/experimental
220 option and may change or be removed in the future. Supported
221 groups are: builtins, parseopt (builtin commands that use
222 - parse-options), main (all commands in libexec directory),
222 + parse-options), deprecated (deprecated builtins),
223 + main (all commands in libexec directory),
224 others (all other commands in `$PATH` that have git- prefix),
225 list-<category> (see categories in command-list.txt),
226 nohelpers (exclude helper commands), alias and config
git.c
+18 -8
@@ -28,6 +28,7 @@
28 #define NEED_WORK_TREE (1<<3)
29 #define DELAY_PAGER_CONFIG (1<<4)
30 #define NO_PARSEOPT (1<<5) /* parse-options is not used */
31 +#define DEPRECATED (1<<6)
32
33 struct cmd_struct {
34 const char *cmd;
@@ -51,7 +52,9 @@ const char git_more_info_string[] =
52
53 static int use_pager = -1;
54
54 -static void list_builtins(struct string_list *list, unsigned int exclude_option);
55 +static void list_builtins(struct string_list *list,
56 + unsigned int include_option,
57 + unsigned int exclude_option);
58
59 static void exclude_helpers_from_list(struct string_list *list)
60 {
@@ -88,7 +91,7 @@ static int list_cmds(const char *spec)
91 int len = sep - spec;
92
93 if (match_token(spec, len, "builtins"))
91 - list_builtins(&list, 0);
94 + list_builtins(&list, 0, 0);
95 else if (match_token(spec, len, "main"))
96 list_all_main_cmds(&list);
97 else if (match_token(spec, len, "others"))
@@ -99,6 +102,8 @@ static int list_cmds(const char *spec)
102 list_aliases(&list);
103 else if (match_token(spec, len, "config"))
104 list_cmds_by_config(&list);
105 + else if (match_token(spec, len, "deprecated"))
106 + list_builtins(&list, DEPRECATED, 0);
107 else if (len > 5 && !strncmp(spec, "list-", 5)) {
108 struct strbuf sb = STRBUF_INIT;
109
@@ -322,7 +327,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
327 if (!strcmp(cmd, "parseopt")) {
328 struct string_list list = STRING_LIST_INIT_DUP;
329
325 - list_builtins(&list, NO_PARSEOPT);
330 + list_builtins(&list, 0, NO_PARSEOPT);
331 for (size_t i = 0; i < list.nr; i++)
332 printf("%s ", list.items[i].string);
333 string_list_clear(&list, 0);
@@ -590,7 +595,7 @@ static struct cmd_struct commands[] = {
595 { "notes", cmd_notes, RUN_SETUP },
596 { "pack-objects", cmd_pack_objects, RUN_SETUP },
597 #ifndef WITH_BREAKING_CHANGES
593 - { "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },
598 + { "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT | DEPRECATED },
599 #endif
600 { "pack-refs", cmd_pack_refs, RUN_SETUP },
601 { "patch-id", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },
@@ -647,7 +652,7 @@ static struct cmd_struct commands[] = {
652 { "verify-tag", cmd_verify_tag, RUN_SETUP },
653 { "version", cmd_version },
654 #ifndef WITH_BREAKING_CHANGES
650 - { "whatchanged", cmd_whatchanged, RUN_SETUP },
655 + { "whatchanged", cmd_whatchanged, RUN_SETUP | DEPRECATED },
656 #endif
657 { "worktree", cmd_worktree, RUN_SETUP },
658 { "write-tree", cmd_write_tree, RUN_SETUP },
@@ -668,11 +673,16 @@ int is_builtin(const char *s)
673 return !!get_builtin(s);
674 }
675
671 -static void list_builtins(struct string_list *out, unsigned int exclude_option)
676 +static void list_builtins(struct string_list *out,
677 + unsigned int include_option,
678 + unsigned int exclude_option)
679 {
680 + if (include_option && exclude_option)
681 + BUG("'include_option' and 'exclude_option' are mutually exclusive");
682 for (size_t i = 0; i < ARRAY_SIZE(commands); i++) {
674 - if (exclude_option &&
675 - (commands[i].option & exclude_option))
683 + if (include_option && !(commands[i].option & include_option))
684 + continue;
685 + if (exclude_option && (commands[i].option & exclude_option))
686 continue;
687 string_list_append(out, commands[i].cmd);
688 }