Subject: [PATCH v2 0/3] permit -h/--help-all in more scenarios

From: "D. Ben Knoble" <[email-masked]>
Date: Sat, 2 Aug 2025 21:26:01 -0400

Changes from v1:
- tweak refactor commit message to indicate no behavior is changed;
- use #define'd constants instead of an enum for internal flags;
- drop controversial [4/4]

QQ: Is there a better way to add --cc's to format-patch than grabbing the right
folks off threads from lore.kernel.org/git? (I could also grab them out of my
mail client.) I assume this is where using tools like b4 starts to really shine?
To be clear: I mean CC's _in addition to_ the use of

    sendemail.ccCmd = perl contrib/contacts/git-contacts

to keep folks that participated in prior rounds of discussion abreast of the
next version.

--------
Original
--------

This series depends on ua/t1517-short-help-tests with some fixes, which
show up in the first patch. Merge that branch to a new topic branch:

    git switch -c topic v2.50.0 # or origin/master
    git merge gitster/ua/t1517-short-help-tests

then apply this series.

This series enables --help-all outside of repository contexts, and
allows -h with other arguments (without breaking existing ls-remote/grep
usage).

It consists of preparatory steps (fixes for a dependency branch;
refactoring to make an internal helper's arguments clearer) followed by
the main commits.

v1: https://lore.kernel.org/git/[email-masked]/
Published-as: https://github.com/benknoble/git/tree/help-all-tweaks

D. Ben Knoble (3):
  t1517: fixup for ua/t1517-short-help-tests
  parse-options: refactor flags for usage_with_options_internal
  builtin: also setup gently for --help-all

 builtin/merge-recursive.c     |  3 ++-
 git.c                         |  2 +-
 parse-options.c               | 30 +++++++++++++++++++++++-------
 t/t1517-outside-repo.sh       | 11 +++++++----
 t/t5200-update-server-info.sh |  2 +-
 usage.c                       |  3 ++-
 6 files changed, 36 insertions(+), 15 deletions(-)

Diff-intervalle contre v1 :
1:  852a4547af ! 1:  7a3e0a601d t1517: fixup for ua/t1517-short-help-tests
    @@ Commit message
     
         - drop spurious message during test
         - fix known breakages that actually work
    +    - fix instaweb marker for known failure
         - fix new t5200 test

    @@ Notes
     
      ## t/t1517-outside-repo.sh ##
     @@
    + 	case "$cmd" in
    + 	archimport | cvsexportcommit | cvsimport | cvsserver | daemon | \
      	difftool--helper | filter-branch | fsck-objects | get-tar-commit-id | \
    - 	http-backend | http-fetch | http-push | init-db | instaweb.sh | \
    +-	http-backend | http-fetch | http-push | init-db | instaweb.sh | \
    ++	http-backend | http-fetch | http-push | init-db | \
      	merge-octopus | merge-one-file | merge-resolve | mergetool | \
     -	mktag | p4 | p4.py | pickaxe | quiltimport | remote-ftp | remote-ftps | \
     -	remote-http | remote-https | replay | request-pull | send-email | \
2:  56665594a8 ! 2:  db74b1eff7 parse-options: name flags passed to usage_with_options_internal
    @@ Metadata
     Author: D. Ben Knoble <[email-masked]>
     
      ## Commit message ##
    -    parse-options: name flags passed to usage_with_options_internal
    +    parse-options: refactor flags for usage_with_options_internal
     
         When reading or editing calls to usage_with_options_internal, it is
         difficult to tell what trailing "0, 0", "0, 1", "1, 0" arguments mean
         (NB there is never a "1, 1" case).
     
    -    Give the flags readable names to improve call-sites.
    +    Give the flags readable names to improve call-sites without changing any
    +    behavior.
     
      ## parse-options.c ##
     @@ parse-options.c: static void free_preprocessed_options(struct option *options)
      	free(options);
      }
      
    -+enum usage_style {
    -+	style_normal = 0,
    -+	style_full = 1,
    -+};
    -+
    -+enum usage_output {
    -+	to_out = 0,
    -+	to_err = 1,
    -+};
    ++#define USAGE_NORMAL 0
    ++#define USAGE_FULL 1
    ++#define USAGE_TO_STDOUT 0
    ++#define USAGE_TO_STDERR 1
     +
      static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *,
      							 const char * const *,
      							 const struct option *,
     -							 int, int);
    -+							 enum usage_style,
    -+							 enum usage_output);
    ++							 int full_usage,
    ++							 int usage_to_stderr);
      
      enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,
      					 const struct option *options,
    @@ parse-options.c: enum parse_opt_result parse_options_step(struct parse_opt_ctx_t
      
      		if (internal_help && !strcmp(arg + 2, "help-all"))
     -			return usage_with_options_internal(ctx, usagestr, options, 1, 0);
    -+			return usage_with_options_internal(ctx, usagestr, options, style_full, to_out);
    ++			return usage_with_options_internal(ctx, usagestr, options,
    ++							   USAGE_FULL, USAGE_TO_STDOUT);
      		if (internal_help && !strcmp(arg + 2, "help"))
      			goto show_usage;
      		switch (parse_long_opt(ctx, arg + 2, options)) {
    @@ parse-options.c: enum parse_opt_result parse_options_step(struct parse_opt_ctx_t
      
       show_usage:
     -	return usage_with_options_internal(ctx, usagestr, options, 0, 0);
    -+	return usage_with_options_internal(ctx, usagestr, options, style_normal, to_out);
    ++	return usage_with_options_internal(ctx, usagestr, options,
    ++					   USAGE_NORMAL, USAGE_TO_STDOUT);
      }
      
      int parse_options_end(struct parse_opt_ctx_t *ctx)
    -@@ parse-options.c: static const struct option *find_option_by_long_name(const struct option *opts,
    - static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx,
    - 							 const char * const *usagestr,
    - 							 const struct option *opts,
    --							 int full, int err)
    -+							 enum usage_style help_style,
    -+							 enum usage_output to_where)
    - {
    - 	const struct option *all_opts = opts;
    --	FILE *outfile = err ? stderr : stdout;
    -+	FILE *outfile = to_where == to_err ? stderr : stdout;
    - 	int need_newline;
    - 
    - 	const char *usage_prefix = _("usage: %s");
    -@@ parse-options.c: static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
    - 	if (!usagestr)
    - 		return PARSE_OPT_HELP;
    - 
    --	if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
    -+	if (to_where != to_err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
    - 		fprintf(outfile, "cat <<\\EOF\n");
    - 
    - 	while (*usagestr) {
    -@@ parse-options.c: static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
    - 				fprintf(outfile, "%s\n", _(opts->help));
    - 			continue;
    - 		}
    --		if (!full && (opts->flags & PARSE_OPT_HIDDEN))
    -+		if (help_style != style_full && (opts->flags & PARSE_OPT_HIDDEN))
    - 			continue;
    - 
    - 		if (need_newline) {
    -@@ parse-options.c: static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
    - 	}
    - 	fputc('\n', outfile);
    - 
    --	if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
    -+	if (to_where != to_err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
    - 		fputs("EOF\n", outfile);
    - 
    - 	return PARSE_OPT_HELP;
     @@ parse-options.c: static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
      void NORETURN usage_with_options(const char * const *usagestr,
      			const struct option *opts)
      {
     -	usage_with_options_internal(NULL, usagestr, opts, 0, 1);
    -+	usage_with_options_internal(NULL, usagestr, opts, style_normal, to_err);
    ++	usage_with_options_internal(NULL, usagestr, opts,
    ++				    USAGE_NORMAL, USAGE_TO_STDERR);
      	exit(129);
      }
      
    @@ parse-options.c: void show_usage_with_options_if_asked(int ac, const char **av,
      {
      	if (ac == 2 && !strcmp(av[1], "-h")) {
     -		usage_with_options_internal(NULL, usagestr, opts, 0, 0);
    -+		usage_with_options_internal(NULL, usagestr, opts, style_normal, to_out);
    ++		usage_with_options_internal(NULL, usagestr, opts,
    ++					    USAGE_NORMAL, USAGE_TO_STDOUT);
      		exit(129);
      	}
      }
3:  352fe87c80 ! 3:  cb4113f77d builtin: also setup gently for --help-all
    @@ Commit message
         The exception is merge-recursive, whose help block doesn't use newer
         APIs.
     
    +    Best-viewed-with: --ignore-space-change
    +
     
      ## Notes ##
         Some usage.c callers, like check-ref-format, probably deserve to be
    @@ parse-options.c: void show_usage_with_options_if_asked(int ac, const char **av,
      				      const struct option *opts)
      {
     -	if (ac == 2 && !strcmp(av[1], "-h")) {
    --		usage_with_options_internal(NULL, usagestr, opts, style_normal, to_out);
    +-		usage_with_options_internal(NULL, usagestr, opts,
    +-					    USAGE_NORMAL, USAGE_TO_STDOUT);
     -		exit(129);
     +	if (ac == 2) {
     +		if (!strcmp(av[1], "-h")) {
    -+			usage_with_options_internal(NULL, usagestr, opts, style_normal, to_out);
    ++			usage_with_options_internal(NULL, usagestr, opts,
    ++						    USAGE_NORMAL, USAGE_TO_STDOUT);
     +			exit(129);
     +		} else if (!strcmp(av[1], "--help-all")) {
    -+			usage_with_options_internal(NULL, usagestr, opts, style_full, to_out);
    ++			usage_with_options_internal(NULL, usagestr, opts,
    ++						    USAGE_FULL, USAGE_TO_STDOUT);
     +			exit(129);
     +		}
      	}
4:  3099d83cdf < -:  ---------- builtins: show help on "-h"/"--help-all" with more than 2 arguments left

base-commit: e4ef0485fd78fcb05866ea78df35796b904e4a8e
prerequisite-patch-id: ffce2dd036e61c8d36485a17321f858e454db874
prerequisite-patch-id: 52539022c824997adfc1be0bed8de6b1851d2187
-- 
2.48.1