git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Git branch outputs usage message on stderr

From
Jeff King <peff@peff.net>
Date
Jan 15, 2025, 17:14 UTC
Message-ID
<20250115171423.GB57018@coredump.intra.peff.net>
In-Reply-To
<c92e7b16-b70d-46f3-9858-2be805c5285f@app.fastmail.com>
On Wed, Jan 15, 2025 at 05:55:19PM +0100, Kristoffer Haugsbakk wrote:
Show 12 quoted lines
> On Wed, Jan 15, 2025, at 16:28, Junio C Hamano wrote:
> > Somebody may want to go over "git help --all" and for each of them
> > try "git $cmd -h >/dev/null" to find those that give the help output
> > to their standard error stream.
> 
>     #!/bin/sh
> 
>     for cmd in $(git --list-cmds=builtins); do
>         git $cmd -h >/dev/null
>     done 2>&1 | grep '^usage: ' \
>         | perl -pe 's/^usage:\s*(\(EXPERIMENTAL!\)\s*)?//; s/^(git\s+[a-zA-Z0-9-]+).*/\1/'
> [...]
We may want:
diff --git a/t/t0012-help.sh b/t/t0012-help.sh
index 1d273d91c2..469cb12eb2 100755
--- a/t/t0012-help.sh
+++ b/t/t0012-help.sh
@@ -255,7 +255,8 @@ do
 		(
 			GIT_CEILING_DIRECTORIES=$(pwd) &&
 			export GIT_CEILING_DIRECTORIES &&
-			test_expect_code 129 git -C sub $builtin -h >output 2>&1
+			test_expect_code 129 git -C sub $builtin -h >output 2>err &&
+			test_must_be_empty err
 		) &&
 		test_grep usage output
 	'

which produces a similar list. In the case of git-branch, it is due to
1dacfbcf13 (branch -h: show usage even in an invalid repository,
2010-10-22). Instead of letting parse-options handle it (which then goes
to stdout), we call usage_with_options(), which is usually for
complaining about a broken option, not showing "-h".

The reason there is that some of the pre-parse_options() setup accesses
the ref store (causing a BUG() if you run "branch -h" outside of a
repository). In this case, I think it can simply be reordered like:

diff --git a/builtin/branch.c b/builtin/branch.c
index 6e7b0cfddb..4617e32fff 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -784,31 +784,28 @@ int cmd_branch(int argc,
 	filter.kind = FILTER_REFS_BRANCHES;
 	filter.abbrev = -1;
 
-	if (argc == 2 && !strcmp(argv[1], "-h"))
-		usage_with_options(builtin_branch_usage, options);
-
 	/*
 	 * Try to set sort keys from config. If config does not set any,
 	 * fall back on default (refname) sorting.
 	 */
 	git_config(git_branch_config, &sorting_options);
 	if (!sorting_options.nr)
 		string_list_append(&sorting_options, "refname");
 
 	track = git_branch_track;
 
+	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
+			     0);
+
 	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
 				   0, &head_oid, NULL);
 	if (!head)
 		die(_("failed to resolve HEAD as a valid ref"));
 	if (!strcmp(head, "HEAD"))
 		filter.detached = 1;
 	else if (!skip_prefix(head, "refs/heads/", &head))
 		die(_("HEAD not found below refs/heads!"));
 
-	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
-			     0);
-
 	if (!delete && !rename && !copy && !edit_description && !new_upstream &&
 	    !show_current && !unset_upstream && argc == 0)
 		list = 1;

Knowing that is safe means confirming manually that setup code is not
needed by parse_options(). E.g., if it were setting defaults the user
could overwrite with an option. In this case neither "head" nor
"filter.detached" is touched by any of the options.

But there are a ton of commands, as you saw, and handling each one would
be a pain. So it would probably be easier to just introduce a variant of
usage_with_options() that writes to stdout (the underlying _internal
function already does so, we'd just need a one-liner wrapper). And then
use that everywhere. Possibly it could even do the argc/argv check, too,
since every call site is going to be doing that itself.

-Peff
Previous: Kristoffer HaugsbakkNext: Junio C Hamano
Message 6 of 27 in “Git branch outputs usage message on stderr”
  1. Jonas KonradJan 15, 2025
  2. Matěj CeplJan 15, 2025
  3. Jonas KonradJan 15, 2025
  4. Junio C HamanoJan 15, 2025
  5. Kristoffer HaugsbakkJan 15, 2025
  6. Jeff KingJan 15, 2025
  7. Junio C HamanoJan 15, 2025
  8. Junio C HamanoJan 15, 2025
  9. Jeff KingJan 15, 2025
  10. Junio C HamanoJan 15, 2025
  11. Jeff KingJan 15, 2025
  12. Junio C HamanoJan 15, 2025
  13. Jeff KingJan 15, 2025
  14. Junio C HamanoJan 15, 2025
  15. Junio C HamanoJan 16, 2025
  16. Jeff KingJan 16, 2025
  17. Junio C HamanoJan 15, 2025
  18. Jeff KingJan 15, 2025
  19. Junio C HamanoJan 15, 2025
  20. Junio C HamanoJan 15, 2025
  21. Kristoffer HaugsbakkJan 15, 2025
  22. Junio C HamanoJan 15, 2025
  23. Jonas KonradJan 15, 2025
  24. Kristoffer HaugsbakkJan 15, 2025
  25. Junio C HamanoJan 15, 2025
  26. Kristoffer HaugsbakkJan 15, 2025
  27. Junio C HamanoJan 15, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.