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

Re: [PATCH 1/5] pretty.c: delimit "%(trailers)" arguments with ","

From
Jeff King <peff@peff.net>
Date
Sep 30, 2017, 06:49 UTC
Message-ID
<20170930064949.23plbzjtw2qupj6b@sigill.intra.peff.net>
In-Reply-To
<20170930062238.87077-2-me@ttaylorr.com>
On Fri, Sep 29, 2017 at 11:22:34PM -0700, Taylor Blau wrote:
Show 10 quoted lines
> In preparation for adding consistent "%(trailers)" atom options to
> `git-for-each-ref(1)`'s "--format" argument, change "%(trailers)" in pretty.c
> to separate sub-arguments with a ",", instead of a ":".
> 
> Multiple sub-arguments are given either as "%(trailers:unfold,only)" or
> "%(trailers:only,unfold)".
> 
> This change disambiguates between "top-level" arguments, and arguments given to
> the trailers atom itself. It is consistent with the behavior of "%(upstream)"
> and "%(push)" atoms.

Great. I hope we'll eventually unify the pretty.c and ref-filter.c syntaxes and implementations, so consistency is good. I'm sure there will be some hackery to keep backwards compatibility in all cases, but the fewer the better. The "every option gets its own colon" scheme was just something I invented, because there was no prior art in pretty.c. I didn't think to check for similar cases in ref-filter.c.

We should be safe to change this unconditionally, without worrying about breaking compatibility. These options were added in 58311c66fd (pretty: support normalization options for %(trailers), 2017-08-15), which hasn't been in any released version yet.

> ---
>  pretty.c                      | 13 ++++++++-----
>  t/t4205-log-pretty-formats.sh |  4 ++--
>  2 files changed, 10 insertions(+), 7 deletions(-)
I think this needs an update to Documentation/pretty-formats.txt. Maybe:
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 6b4a12f028..40948d925b 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -199,11 +199,13 @@ endif::git-rev-list[]
   than given and there are spaces on its left, use those spaces
 - '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'
   respectively, but padding both sides (i.e. the text is centered)
-- %(trailers): display the trailers of the body as interpreted by
-  linkgit:git-interpret-trailers[1]. If the `:only` option is given,
-  omit non-trailer lines from the trailer block.  If the `:unfold`
-  option is given, behave as if interpret-trailer's `--unfold` option
-  was given. E.g., `%(trailers:only:unfold)` to do both.
+- %(trailers[:options]): display the trailers of the body as interpreted
+  by linkgit:git-interpret-trailers[1]. The `trailers` string may be
+  followed by a colon and zero or more comma-separated options. If the
+  `only` option is given, omit non-trailer lines from the trailer block.
+  If the `unfold` option is given, behave as if interpret-trailer's
+  `--unfold` option was given.  E.g., `%(trailers:only,unfold)` to do
+  both.
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will

diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index ec5f530102..977472f539 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -588,8 +588,8 @@ test_expect_success '%(trailers:unfold) unfolds trailers' '
 '
 
 test_expect_success ':only and :unfold work together' '
-	git log --no-walk --pretty="%(trailers:only:unfold)" >actual &&
-	git log --no-walk --pretty="%(trailers:unfold:only)" >reverse &&
+	git log --no-walk --pretty="%(trailers:only,unfold)" >actual &&
+	git log --no-walk --pretty="%(trailers:unfold,only)" >reverse &&
 	test_cmp actual reverse &&
 	{
 		grep -v patch.description <trailers | unfold &&

> diff --git a/pretty.c b/pretty.c
> index 0e23fe3c0..68f736912 100644
> --- a/pretty.c
> +++ b/pretty.c
> @@ -1265,11 +1265,14 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
>  
>  	if (skip_prefix(placeholder, "(trailers", &arg)) {
>  		struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
> -		while (*arg == ':') {
> -			if (skip_prefix(arg, ":only", &arg))
> -				opts.only_trailers = 1;
> -			else if (skip_prefix(arg, ":unfold", &arg))
> -				opts.unfold = 1;
> +		if (skip_prefix(arg, ":", &arg)) {
> +			while (*arg != ')') {
> +				skip_prefix(arg, ",", &arg);
> +				if (skip_prefix(arg, "only", &arg))
> +					opts.only_trailers = 1;
> +				else if (skip_prefix(arg, "unfold", &arg))
> +					opts.unfold = 1;
> +			}
>  		}

Do we always make forward progress with this loop condition?

If arg doesn't start with a close-paren, we'll keep looping. But if it's
not a comma or one of the option strings, then skip_prefix() won't move
the pointer forward. So I think "%(trailers:foo)" would loop forever.

The original would complain about that and even almost-right things like
"%(trailers:only-the-lonely", though it's pretty subtle. The atom
parsers in ref-filter.c use string_list_split() which is way more
readable and obvious. But I don't think we can do that here. ref-filter
has a separate parsing step, so it can afford to be slow. Here we're
parsing the pretty format individually for each commit in "git log", and
allocations would be noticeable.

Here's what I came up with:

  static int match_placeholder_arg(const char *to_parse,
  				 const char *candidate,
  				 const char **end)
  {
  	const char *p;
  	if (!skip_prefix(to_parse, candidate, &p))
  		return 0;
  	if (*p == ',') {
  		*end = p + 1;
  		return 1;
  	}
  	if (*p == ')') {
  		*end = p;
  		return 1;
  	}
  	return 0;
  }

  ...

  	if (skip_prefix(placeholder, "(trailers", &arg)) {
  		struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
  		if (*arg == ':') {
  			arg++;
  			for (;;) {
  				if (match_placeholder_arg(arg, "only", &arg))
  					opts.only_trailers = 1;
  				else if (match_placeholder_arg(arg, "unfold", &arg))
  					opts.unfold = 1;
  				else
  					break;
  			}
  		}
  		if (*arg == ')') {
  			format_trailers_from_commit(sb, msg + c->subject_off, &opts);
  			return arg - placeholder + 1;
  		}
  	}

I hate the "for (;;)", but at least it's easy to see that each iteration
either makes progress or breaks out of the loop.

-Peff
Previous: Taylor BlauNext: Taylor Blau
Message 3 of 78 in “Support %(trailers) arguments in for-each-ref(1)”
  1. 0/5 Support %(trailers) arguments in for-each-ref(1)Taylor Blau, Sep 30, 2017
  2. 1/5 pretty.c: delimit "%(trailers)" arguments with ","Taylor Blau, Sep 30, 2017
  3. Jeff KingSep 30, 2017
  4. 2/5 t6300: refactor %(trailers) testsTaylor Blau, Sep 30, 2017
  5. Jeff KingSep 30, 2017
  6. 3/5 ref-filter.c: add trailer options to used_atomTaylor Blau, Sep 30, 2017
  7. Jeff KingSep 30, 2017
  8. 4/5 ref-filter.c: use trailer_opts to format trailersTaylor Blau, Sep 30, 2017
  9. Jeff KingSep 30, 2017
  10. Junio C HamanoOct 1, 2017
  11. Junio C HamanoOct 1, 2017
  12. Jeff KingOct 2, 2017
  13. Taylor BlauOct 2, 2017
  14. 5/5 ref-filter.c: parse trailers arguments with %(contents) atomTaylor Blau, Sep 30, 2017
  15. Jeff KingSep 30, 2017
  16. Jeff KingSep 30, 2017
  17. 0/6 Support %(trailers) arguments in for-each-ref(1)Taylor Blau, Sep 30, 2017
  18. 1/6 pretty.c: delimit "%(trailers)" arguments with ","Taylor Blau, Sep 30, 2017
  19. 2/6 t6300: refactor %(trailers) testsTaylor Blau, Sep 30, 2017
  20. 4/6 doc: use modern "`"-style code fencingTaylor Blau, Sep 30, 2017
  21. 5/6 ref-filter.c: use trailer_opts to format trailersTaylor Blau, Sep 30, 2017
  22. 3/6 doc: 'trailers' is the preferred way to format trailersTaylor Blau, Sep 30, 2017
  23. 6/6 ref-filter.c: parse trailers arguments with %(contents) atomTaylor Blau, Sep 30, 2017
  24. Taylor BlauOct 1, 2017
  25. 1/6 pretty.c: delimit "%(trailers)" arguments with ","Taylor Blau, Oct 1, 2017
  26. 2/6 t6300: refactor %(trailers) testsTaylor Blau, Oct 1, 2017
  27. 3/6 doc: 'trailers' is the preferred way to format trailersTaylor Blau, Oct 1, 2017
  28. 4/6 doc: use modern "`"-style code fencingTaylor Blau, Oct 1, 2017
  29. 5/6 ref-filter.c: use trailer_opts to format trailersTaylor Blau, Oct 1, 2017
  30. 6/6 ref-filter.c: parse trailers arguments with %(contents) atomTaylor Blau, Oct 1, 2017
  31. Taylor BlauOct 1, 2017
  32. 1/6 pretty.c: delimit "%(trailers)" arguments with ","Taylor Blau, Oct 1, 2017
  33. 2/6 t6300: refactor %(trailers) testsTaylor Blau, Oct 1, 2017
  34. Junio C HamanoOct 2, 2017
  35. 3/6 doc: 'trailers' is the preferred way to format trailersTaylor Blau, Oct 1, 2017
  36. 4/6 doc: use modern "`"-style code fencingTaylor Blau, Oct 1, 2017
  37. Junio C HamanoOct 1, 2017
  38. Taylor BlauOct 2, 2017
  39. Junio C HamanoOct 2, 2017
  40. Jeff KingOct 2, 2017
  41. 5/6 ref-filter.c: use trailer_opts to format trailersTaylor Blau, Oct 1, 2017
  42. Junio C HamanoOct 2, 2017
  43. 6/6 ref-filter.c: parse trailers arguments with %(contents) atomTaylor Blau, Oct 1, 2017
  44. Junio C HamanoOct 2, 2017
  45. Junio C HamanoOct 2, 2017
  46. Jeff KingOct 2, 2017
  47. Taylor BlauOct 2, 2017
  48. 1/6 pretty.c: delimit "%(trailers)" arguments with ","Taylor Blau, Oct 2, 2017
  49. 2/6 t6300: refactor %(trailers) testsTaylor Blau, Oct 2, 2017
  50. 3/6 doc: 'trailers' is the preferred way to format trailersTaylor Blau, Oct 2, 2017
  51. 5/6 ref-filter.c: use trailer_opts to format trailersTaylor Blau, Oct 2, 2017
  52. 4/6 doc: use modern "`"-style code quotingTaylor Blau, Oct 2, 2017
  53. 6/6 ref-filter.c: parse trailers arguments with %(contents) atomTaylor Blau, Oct 2, 2017
  54. Junio C HamanoOct 2, 2017
  55. Junio C HamanoOct 2, 2017
  56. Taylor BlauOct 2, 2017
  57. Jeff KingOct 2, 2017
  58. Taylor BlauOct 2, 2017
  59. Jeff KingOct 2, 2017
  60. Taylor BlauOct 2, 2017
  61. 1/7 pretty.c: delimit "%(trailers)" arguments with ","Taylor Blau, Oct 2, 2017
  62. 2/7 t4205: unfold across multiple linesTaylor Blau, Oct 2, 2017
  63. 3/7 doc: 'trailers' is the preferred way to format trailersTaylor Blau, Oct 2, 2017
  64. 4/7 doc: use modern "`"-style code quotingTaylor Blau, Oct 2, 2017
  65. 6/7 ref-filter.c: use trailer_opts to format trailersTaylor Blau, Oct 2, 2017
  66. 7/7 ref-filter.c: parse trailers arguments with %(contents) atomTaylor Blau, Oct 2, 2017
  67. Jeff KingOct 2, 2017
  68. Junio C HamanoOct 2, 2017
  69. Taylor BlauOct 2, 2017
  70. Junio C HamanoOct 2, 2017
  71. 5/7 t6300: refactor %(trailers) testsTaylor Blau, Oct 2, 2017
  72. Jeff KingOct 2, 2017
  73. Junio C HamanoOct 3, 2017
  74. Jeff KingOct 3, 2017
  75. Junio C HamanoOct 3, 2017
  76. Junio C HamanoOct 2, 2017
  77. Junio C HamanoOct 2, 2017
  78. Taylor BlauOct 2, 2017

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.