{"thread":{"id":"66168","subject":"[PATCH 0/4] format-rev: add --abbrev, --color, and --date","startedAt":"2026-08-13T17:24:26Z","lastAt":"2026-09-03T15:22:53Z","messageCount":20,"participants":["kristofferhaugsbakk@fastmail.com","Junio C Hamano","Kristoffer Haugsbakk","Ben Knoble"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"550541","messageId":"CV_format-rev_three_more_opts.b80@msgid.xyz","threadId":"66168","inReplyTo":null,"subject":"[PATCH 0/4] format-rev: add --abbrev, --color, and --date","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-13T17:23:56Z","receivedAt":"2026-08-13T17:24:26Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nTopic name: kh/format-rev-more-options\n\nTopic summary: Add three more options for controlling the formatting. Also\ndo some minor refactoring and text fixes as preparatory steps.\n\n[1/4] format-rev: use lower case for opts description\n[2/4] format-rev: factor option variables into a struct\n[3/4] doc: rev-list-options.adoc: factor out --date alts\n[4/4] format-rev: learn --abbrev, --color, and --date\n\n Documentation/git-format-rev.adoc             | 44 +++++++++-\n .../rev-list-option-date-alternatives.adoc    | 55 ++++++++++++\n Documentation/rev-list-options.adoc           | 56 +-----------\n builtin/name-rev.c                            | 88 ++++++++++++-------\n t/t6120-describe.sh                           | 44 ++++++++++\n 5 files changed, 195 insertions(+), 92 deletions(-)\n create mode 100644 Documentation/rev-list-option-date-alternatives.adoc\n\n\nbase-commit: 010afd3166ddc64c9863b1506f12cbcdda0d4ea1\n-- \n2.54.0.22.g9e26862b904\n\n"},{"id":"550542","messageId":"lower_case_opts_descrps.b81@msgid.xyz","threadId":"66168","inReplyTo":"CV_format-rev_three_more_opts.b80@msgid.xyz","subject":"[PATCH 1/4] format-rev: use lower case for opts description","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-13T17:23:57Z","receivedAt":"2026-08-13T17:24:44Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe option descriptions use a mix of initial capital and lower case\nletters. Lower case is the correct style.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n builtin/name-rev.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 60cbbfb4b7d..254c88199fd 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -833,12 +833,12 @@ int cmd_format_rev(int argc,\n \t\tOPT_STRING_LIST(0, \"notes\", &notes, N_(\"notes\"),\n \t\t\t\tN_(\"display notes for pretty format\")),\n \t\tOPT_CALLBACK_F('z', \"null\", &nul_data, N_(\"z\"),\n-\t\t\t       N_(\"Use NUL for input and output termination\"),\n+\t\t\t       N_(\"use NUL for input and output termination\"),\n \t\t\t       PARSE_OPT_NOARG | PARSE_OPT_NONEG, format_nul_cb),\n \t\tOPT_BOOL(0, \"null-input\", &nul_data.nul_input,\n-\t\t\t N_(\"Use NUL for input termination\")),\n+\t\t\t N_(\"use NUL for input termination\")),\n \t\tOPT_BOOL(0, \"null-output\", &nul_data.nul_output,\n-\t\t\t N_(\"Use NUL for output termination\")),\n+\t\t\t N_(\"use NUL for output termination\")),\n \t\tOPT_END(),\n \t};\n \n-- \n2.54.0.22.g9e26862b904\n\n"},{"id":"550543","messageId":"factor_opts_into_struct.b82@msgid.xyz","threadId":"66168","inReplyTo":"CV_format-rev_three_more_opts.b80@msgid.xyz","subject":"[PATCH 2/4] format-rev: factor option variables into a struct","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-13T17:23:58Z","receivedAt":"2026-08-13T17:25:03Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWe will in two commits add three more options to this command.\nLet’s prepare for that by moving option variables into a struct\nso that we get less local variables.\n\nThis allows us to inline `format_nul_data` into this new\nstructure. Let’s also rename `stdin_mode_arg` to `stdin_mode`.\n(We couldn’t use `stdin_mode` before because of the enumeration\nwith the same name.)\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n builtin/name-rev.c | 44 +++++++++++++++++++++++---------------------\n 1 file changed, 23 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 254c88199fd..7d824aa1c5d 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -772,16 +772,19 @@ int cmd_name_rev(int argc,\n \treturn 0;\n }\n \n-struct format_nul_data {\n+struct format_rev_data {\n+\tconst char *format;\n+\tconst char *stdin_mode;\n \tbool nul_input;\n \tbool nul_output;\n+\tstruct string_list notes;\n };\n \n static int format_nul_cb(const struct option *option,\n \t\t\t const char *arg,\n \t\t\t int unset)\n {\n-\tstruct format_nul_data *data = option->value;\n+\tstruct format_rev_data *data = option->value;\n \tdata->nul_input = 1;\n \tdata->nul_output = 1;\n \tBUG_ON_OPT_NEG(unset);\n@@ -813,31 +816,30 @@ int cmd_format_rev(int argc,\n \t\t   const char *prefix,\n \t\t   struct repository *repo UNUSED)\n {\n-\tconst char *format = NULL;\n+\tstruct format_rev_data data = {\n+\t\tNULL, NULL, 0, 0, STRING_LIST_INIT_NODUP\n+\t};\n \tenum stdin_mode stdin_mode;\n-\tconst char *stdin_mode_arg = NULL;\n-\tstruct format_nul_data nul_data = { 0, 0 };\n \tchar output_terminator;\n \tstrbuf_getline_fn getline_fn;\n \tstruct display_notes_opt format_notes_opt;\n \tstruct rev_info format_rev = REV_INFO_INIT;\n \tstruct pretty_format format_pp = { 0 };\n-\tstruct string_list notes = STRING_LIST_INIT_NODUP;\n \tstruct strbuf scratch_buf = STRBUF_INIT;\n \tstruct command cmd;\n \tstruct option opts[] = {\n-\t\tOPT_STRING(0, \"format\", &format, N_(\"format\"),\n+\t\tOPT_STRING(0, \"format\", &data.format, N_(\"format\"),\n \t\t\t   N_(\"pretty format to use\")),\n-\t\tOPT_STRING(0, \"stdin-mode\", &stdin_mode_arg, N_(\"stdin-mode\"),\n+\t\tOPT_STRING(0, \"stdin-mode\", &data.stdin_mode, N_(\"stdin-mode\"),\n \t\t\t   N_(\"how revs are processed\")),\n-\t\tOPT_STRING_LIST(0, \"notes\", &notes, N_(\"notes\"),\n+\t\tOPT_STRING_LIST(0, \"notes\", &data.notes, N_(\"notes\"),\n \t\t\t\tN_(\"display notes for pretty format\")),\n-\t\tOPT_CALLBACK_F('z', \"null\", &nul_data, N_(\"z\"),\n+\t\tOPT_CALLBACK_F('z', \"null\", &data, N_(\"z\"),\n \t\t\t       N_(\"use NUL for input and output termination\"),\n \t\t\t       PARSE_OPT_NOARG | PARSE_OPT_NONEG, format_nul_cb),\n-\t\tOPT_BOOL(0, \"null-input\", &nul_data.nul_input,\n+\t\tOPT_BOOL(0, \"null-input\", &data.nul_input,\n \t\t\t N_(\"use NUL for input termination\")),\n-\t\tOPT_BOOL(0, \"null-output\", &nul_data.nul_output,\n+\t\tOPT_BOOL(0, \"null-output\", &data.nul_output,\n \t\t\t N_(\"use NUL for output termination\")),\n \t\tOPT_END(),\n \t};\n@@ -849,18 +851,18 @@ int cmd_format_rev(int argc,\n \t\tusage_with_options(format_rev_usage, opts);\n \t}\n \n-\tif (!format)\n+\tif (!data.format)\n \t\tdie(_(\"'%s' is required\"), \"--format\");\n-\tif (!stdin_mode_arg)\n+\tif (!data.stdin_mode)\n \t\tdie(_(\"'%s' is required\"), \"--stdin-mode\");\n \n-\tgetline_fn = nul_data.nul_input ? strbuf_getline_nul : strbuf_getline_lf;\n-\toutput_terminator = nul_data.nul_output ? '\\0' : '\\n';\n+\tgetline_fn = data.nul_input ? strbuf_getline_nul : strbuf_getline_lf;\n+\toutput_terminator = data.nul_output ? '\\0' : '\\n';\n \n \tinit_display_notes(&format_notes_opt);\n-\tstdin_mode = parse_stdin_mode(stdin_mode_arg);\n+\tstdin_mode = parse_stdin_mode(data.stdin_mode);\n \n-\tget_commit_format(format, &format_rev);\n+\tget_commit_format(data.format, &format_rev);\n \tformat_pp.ctx.rev = &format_rev;\n \tformat_pp.ctx.fmt = format_rev.commit_format;\n \tformat_pp.ctx.abbrev = format_rev.abbrev;\n@@ -868,13 +870,13 @@ int cmd_format_rev(int argc,\n \tformat_pp.ctx.date_mode = format_rev.date_mode;\n \tformat_pp.ctx.color = GIT_COLOR_AUTO;\n \n-\tuserformat_find_requirements(format,\n+\tuserformat_find_requirements(data.format,\n \t\t\t\t     &format_pp.want);\n \tif (format_pp.want.notes) {\n \t\tint ignore_show_notes = 0;\n \t\tstruct string_list_item *n;\n \n-\t\tfor_each_string_list_item(n, &notes)\n+\t\tfor_each_string_list_item(n, &data.notes)\n \t\t\tenable_ref_display_notes(&format_notes_opt,\n \t\t\t\t\t\t &ignore_show_notes,\n \t\t\t\t\t\t n->string);\n@@ -934,7 +936,7 @@ int cmd_format_rev(int argc,\n \t}\n \n \tstrbuf_release(&scratch_buf);\n-\tstring_list_clear(&notes, 0);\n+\tstring_list_clear(&data.notes, 0);\n \trelease_display_notes(&format_notes_opt);\n \treturn 0;\n }\n-- \n2.54.0.22.g9e26862b904\n\n"},{"id":"550544","messageId":"doc_--date_alts.b83@msgid.xyz","threadId":"66168","inReplyTo":"CV_format-rev_three_more_opts.b80@msgid.xyz","subject":"[PATCH 3/4] doc: rev-list-options.adoc: factor out --date alts","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-13T17:23:59Z","receivedAt":"2026-08-13T17:25:23Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWe will introduce `--date` to git-format-rev(1) in the next commit and\nwill need to add it to the documentation. Let’s factor out the option\nalternatives so that it can be included in git-format-rev(1).\n\nThe initial paragraph of this option mentions things like git-log(1).\nWe could make it fit in git-format-rev(1) while not changing it for\ngit-rev-list(1) and related commands with some conditionals like\n`ifndef`, but writing a dedicated paragraph is simple enough.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n .../rev-list-option-date-alternatives.adoc    | 55 ++++++++++++++++++\n Documentation/rev-list-options.adoc           | 56 +------------------\n 2 files changed, 56 insertions(+), 55 deletions(-)\n create mode 100644 Documentation/rev-list-option-date-alternatives.adoc\n\ndiff --git a/Documentation/rev-list-option-date-alternatives.adoc b/Documentation/rev-list-option-date-alternatives.adoc\nnew file mode 100644\nindex 00000000000..141570b1059\n--- /dev/null\n+++ b/Documentation/rev-list-option-date-alternatives.adoc\n@@ -0,0 +1,55 @@\n+--\n+`--date=relative` shows dates relative to the current time,\n+e.g. ``2 hours ago''. The `-local` option has no effect for\n+`--date=relative`.\n+\n+`--date=local` is an alias for `--date=default-local`.\n+\n+`--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.\n+The differences to the strict ISO 8601 format are:\n+\n+\t- a space instead of the `T` date/time delimiter\n+\t- a space between time and time zone\n+\t- no colon between hours and minutes of the time zone\n+\n+`--date=iso-strict` (or `--date=iso8601-strict`) shows timestamps in strict\n+ISO 8601 format.\n+\n+`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822\n+format, often found in email messages.\n+\n+`--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.\n+\n+`--date=raw` shows the date as seconds since the epoch (1970-01-01\n+00:00:00 UTC), followed by a space, and then the timezone as an offset\n+from UTC (a `+` or `-` with four digits; the first two are hours, and\n+the second two are minutes). I.e., as if the timestamp were formatted\n+with `strftime(\"%s %z\")`).\n+Note that the `-local` option does not affect the seconds-since-epoch\n+value (which is always measured in UTC), but does switch the accompanying\n+timezone value.\n+\n+`--date=human` shows the timezone if the timezone does not match the\n+current time-zone, and doesn't print the whole date if that matches\n+(ie skip printing year for dates that are \"this year\", but also skip\n+the whole date itself if it's in the last few days and we can just say\n+what weekday it was).  For older dates the hour and minute is also\n+omitted.\n+\n+`--date=unix` shows the date as a Unix epoch timestamp (seconds since\n+1970).  As with `--raw`, this is always in UTC and therefore `-local`\n+has no effect.\n+\n+`--date=format:<format>` feeds the _<format>_ to your system `strftime`,\n+except for `%s`, `%z`, and `%Z`, which are handled internally.\n+Use `--date=format:%c` to show the date in your system locale's\n+preferred format.  See the `strftime`(3) manual for a complete list of\n+format placeholders. When using `-local`, the correct syntax is\n+`--date=format-local:<format>`.\n+\n+`--date=default` is the default format, and is based on ctime(3)\n+output.  It shows a single line with three-letter day of the week,\n+three-letter month, day-of-month, hour-minute-seconds in \"HH:MM:SS\"\n+format, followed by 4-digit year, plus timezone information, unless\n+the local time zone is used, e.g. `Thu Jan 1 00:00:00 1970 +0000`.\n+--\ndiff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc\nindex fd831f0ec64..6e6093f4747 100644\n--- a/Documentation/rev-list-options.adoc\n+++ b/Documentation/rev-list-options.adoc\n@@ -1132,61 +1132,7 @@ include::pretty-options.adoc[]\n \tauthor's). If `-local` is appended to the format (e.g.,\n \t`iso-local`), the user's local time zone is used instead.\n +\n---\n-`--date=relative` shows dates relative to the current time,\n-e.g. ``2 hours ago''. The `-local` option has no effect for\n-`--date=relative`.\n-\n-`--date=local` is an alias for `--date=default-local`.\n-\n-`--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.\n-The differences to the strict ISO 8601 format are:\n-\n-\t- a space instead of the `T` date/time delimiter\n-\t- a space between time and time zone\n-\t- no colon between hours and minutes of the time zone\n-\n-`--date=iso-strict` (or `--date=iso8601-strict`) shows timestamps in strict\n-ISO 8601 format.\n-\n-`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822\n-format, often found in email messages.\n-\n-`--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.\n-\n-`--date=raw` shows the date as seconds since the epoch (1970-01-01\n-00:00:00 UTC), followed by a space, and then the timezone as an offset\n-from UTC (a `+` or `-` with four digits; the first two are hours, and\n-the second two are minutes). I.e., as if the timestamp were formatted\n-with `strftime(\"%s %z\")`).\n-Note that the `-local` option does not affect the seconds-since-epoch\n-value (which is always measured in UTC), but does switch the accompanying\n-timezone value.\n-\n-`--date=human` shows the timezone if the timezone does not match the\n-current time-zone, and doesn't print the whole date if that matches\n-(ie skip printing year for dates that are \"this year\", but also skip\n-the whole date itself if it's in the last few days and we can just say\n-what weekday it was).  For older dates the hour and minute is also\n-omitted.\n-\n-`--date=unix` shows the date as a Unix epoch timestamp (seconds since\n-1970).  As with `--raw`, this is always in UTC and therefore `-local`\n-has no effect.\n-\n-`--date=format:<format>` feeds the _<format>_ to your system `strftime`,\n-except for `%s`, `%z`, and `%Z`, which are handled internally.\n-Use `--date=format:%c` to show the date in your system locale's\n-preferred format.  See the `strftime`(3) manual for a complete list of\n-format placeholders. When using `-local`, the correct syntax is\n-`--date=format-local:<format>`.\n-\n-`--date=default` is the default format, and is based on ctime(3)\n-output.  It shows a single line with three-letter day of the week,\n-three-letter month, day-of-month, hour-minute-seconds in \"HH:MM:SS\"\n-format, followed by 4-digit year, plus timezone information, unless\n-the local time zone is used, e.g. `Thu Jan 1 00:00:00 1970 +0000`.\n---\n+include::rev-list-option-date-alternatives.adoc[]\n \n ifdef::git-rev-list[]\n `--header`::\n-- \n2.54.0.22.g9e26862b904\n\n"},{"id":"550545","messageId":"format-rev_three_more_opts.b84@msgid.xyz","threadId":"66168","inReplyTo":"CV_format-rev_three_more_opts.b80@msgid.xyz","subject":"[PATCH 4/4] format-rev: learn --abbrev, --color, and --date","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-13T17:24:00Z","receivedAt":"2026-08-13T17:25:40Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nAdd three more options for controlling the formatting.\n\nThis does not complete all the pretty formatting knobs for this command\nrelative to e.g. git-log(1), but it does add the most important ones, in\nmy opinion. We can see which are missing by taking a look at\n`Documentation/pretty-options.adoc`:\n\n• `--encoding=<encoding>`\n• `--show-signature`\n• `--expand-tabs=<n>`\n\n***\n\nWe could add these options to the command synopsis, but let’s instead\nsimplify the synopsis to just mention the mandatory options and stuff\nthe other ones into `[<options>]`. I don’t think a long command synopsis\nline is useful. And this way the two mandatory options stand out more.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    > We can see which are missing by taking a look at\n    \n    Or am I missing some?\n\n Documentation/git-format-rev.adoc | 44 ++++++++++++++++++++++++++++---\n builtin/name-rev.c                | 42 ++++++++++++++++++++---------\n t/t6120-describe.sh               | 44 +++++++++++++++++++++++++++++++\n 3 files changed, 115 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/git-format-rev.adoc b/Documentation/git-format-rev.adoc\nindex 505a52feccd..1a06ccbf9b8 100644\n--- a/Documentation/git-format-rev.adoc\n+++ b/Documentation/git-format-rev.adoc\n@@ -9,7 +9,7 @@ git-format-rev - EXPERIMENTAL: Pretty format revisions on demand\n SYNOPSIS\n --------\n [synopsis]\n-(EXPERIMENTAL!) git format-rev --stdin-mode=<mode> --format=<pretty> [--[no-]notes=<ref>] [-z] [--[no-]null-output] [--[no-]null-input]\n+(EXPERIMENTAL!) git format-rev [<options>] --stdin-mode=<mode> --format=<pretty>\n \n DESCRIPTION\n -----------\n@@ -33,8 +33,8 @@ OPTIONS\n The argument `rev` is also accepted.\n \n `text`;; Formats all commit object names found in freeform text. These\n-\tmust be full object names, i.e. abbreviated hexadecimal object\n-\tnames will not be interpreted.\n+\tmust be full object names, i.e. abbreviated hexadecimal (_hex_)\n+\tobject names will not be interpreted.\n +\n Anything that is parsed as an object name but that is not found to be a\n commit object name is left alone (echoed).\n@@ -76,6 +76,44 @@ This is useful if the output could contain newlines, for example if the\n +\n This is useful if the input revision expressions could contain newlines.\n \n+`--color[=<when>]`::\n+`--no-color`::\n+\tRespect color formatting. The default color behavior is\n+\t`auto`. Bare `--color` is the same as `--color=always`.\n++\n+Giving `--no-color` is the same as `--color=never`.\n++\n+_<when>_ must be one of:\n++\n+--\n+`always`;;\n+\tAlways use color, even if the output is something like a file.\n+`never`;;\n+\tNever use color.\n+`auto`;;\n+\tUse color when the output is a terminal but not when the output\n+\tis something like a file.\n+--\n+\n+`--abbrev[=<n>]`::\n+`--no-abbrev`::\n+\tAbbreviate the commit hex output. Without _<n>_ it will find the\n+\tminimum length which can describe the commit uniquely, with some\n+\textra slack. Giving _<n>_ specifies the minimum length; a longer\n+\tlength will be used if needed.\n++\n+Giving `--no-abbrev` will turn off abbreviation, showing the full commit\n+hex output.\n++\n+Note that some pretty formats use `--abbrev`. This behavior can be\n+controlled with these two options.\n+\n+`--date=<format>`::\n+\tDate format for pretty formats. Note that date atoms like `%aI`\n+\tare not affected. This option cannot be negated.\n++\n+include::rev-list-option-date-alternatives.adoc[]\n+\n [[io]]\n INPUT AND OUTPUT FORMAT\n -----------------------\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 7d824aa1c5d..0c9014ca594 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -21,6 +21,7 @@\n #include \"revision.h\"\n #include \"notes.h\"\n #include \"write-or-die.h\"\n+#include \"date.h\"\n \n /*\n  * One day.  See the 'name a rev shortly after epoch' test in t6120 when\n@@ -778,6 +779,8 @@ struct format_rev_data {\n \tbool nul_input;\n \tbool nul_output;\n \tstruct string_list notes;\n+\tstruct rev_info rev;\n+\tint color;\n };\n \n static int format_nul_cb(const struct option *option,\n@@ -792,6 +795,17 @@ static int format_nul_cb(const struct option *option,\n \treturn 0;\n }\n \n+static int date_cb(const struct option *option,\n+\t\t   const char *arg,\n+\t\t   int unset)\n+{\n+\tstruct rev_info *data = option->value;\n+\tparse_date_format(arg, &data->date_mode);\n+\tdata->date_mode_explicit = 1;\n+\tBUG_ON_OPT_NEG(unset);\n+\treturn 0;\n+}\n+\n static enum stdin_mode parse_stdin_mode(const char *stdin_mode)\n {\n \tif (!strcmp(stdin_mode, \"text\"))\n@@ -805,9 +819,8 @@ static enum stdin_mode parse_stdin_mode(const char *stdin_mode)\n }\n \n static char const *const format_rev_usage[] = {\n-\tN_(\"(EXPERIMENTAL!) git format-rev --stdin-mode=<mode> \"\n-\t   \"--format=<pretty> [--[no-]notes=<ref>] \"\n-\t   \"[-z] [--[no-]null-output] [--[no-]null-input]\"),\n+\tN_(\"(EXPERIMENTAL!) git format-rev [<options>] \"\n+\t   \"--stdin-mode=<mode> --format=<pretty>\"),\n \tNULL\n };\n \n@@ -817,13 +830,13 @@ int cmd_format_rev(int argc,\n \t\t   struct repository *repo UNUSED)\n {\n \tstruct format_rev_data data = {\n-\t\tNULL, NULL, 0, 0, STRING_LIST_INIT_NODUP\n+\t\tNULL, NULL, 0, 0, STRING_LIST_INIT_NODUP,\n+\t\tREV_INFO_INIT, GIT_COLOR_AUTO\n \t};\n \tenum stdin_mode stdin_mode;\n \tchar output_terminator;\n \tstrbuf_getline_fn getline_fn;\n \tstruct display_notes_opt format_notes_opt;\n-\tstruct rev_info format_rev = REV_INFO_INIT;\n \tstruct pretty_format format_pp = { 0 };\n \tstruct strbuf scratch_buf = STRBUF_INIT;\n \tstruct command cmd;\n@@ -834,6 +847,11 @@ int cmd_format_rev(int argc,\n \t\t\t   N_(\"how revs are processed\")),\n \t\tOPT_STRING_LIST(0, \"notes\", &data.notes, N_(\"notes\"),\n \t\t\t\tN_(\"display notes for pretty format\")),\n+\t\tOPT__ABBREV(&data.rev.abbrev),\n+\t\tOPT__COLOR(&data.color, N_(\"use colored output\")),\n+\t\tOPT_CALLBACK_F(0, \"date\", &data.rev, N_(\"date\"),\n+\t\t\t       N_(\"date format\"),\n+\t\t\t       PARSE_OPT_NONEG, date_cb),\n \t\tOPT_CALLBACK_F('z', \"null\", &data, N_(\"z\"),\n \t\t\t       N_(\"use NUL for input and output termination\"),\n \t\t\t       PARSE_OPT_NOARG | PARSE_OPT_NONEG, format_nul_cb),\n@@ -862,13 +880,13 @@ int cmd_format_rev(int argc,\n \tinit_display_notes(&format_notes_opt);\n \tstdin_mode = parse_stdin_mode(data.stdin_mode);\n \n-\tget_commit_format(data.format, &format_rev);\n-\tformat_pp.ctx.rev = &format_rev;\n-\tformat_pp.ctx.fmt = format_rev.commit_format;\n-\tformat_pp.ctx.abbrev = format_rev.abbrev;\n-\tformat_pp.ctx.date_mode_explicit = format_rev.date_mode_explicit;\n-\tformat_pp.ctx.date_mode = format_rev.date_mode;\n-\tformat_pp.ctx.color = GIT_COLOR_AUTO;\n+\tget_commit_format(data.format, &data.rev);\n+\tformat_pp.ctx.rev = &data.rev;\n+\tformat_pp.ctx.fmt = data.rev.commit_format;\n+\tformat_pp.ctx.abbrev = data.rev.abbrev;\n+\tformat_pp.ctx.date_mode_explicit = data.rev.date_mode_explicit;\n+\tformat_pp.ctx.date_mode = data.rev.date_mode;\n+\tformat_pp.ctx.color = data.color;\n \n \tuserformat_find_requirements(data.format,\n \t\t\t\t     &format_pp.want);\ndiff --git a/t/t6120-describe.sh b/t/t6120-describe.sh\nindex 7a7c46658a3..2621edb5937 100755\n--- a/t/t6120-describe.sh\n+++ b/t/t6120-describe.sh\n@@ -1017,4 +1017,48 @@ do\n \t'\n done <stdin-modes\n \n+format_rev_cmp_log () {\n+\topts=\"$1\"\n+\tformat=reference\n+\tcat >input <<-\\EOF &&\n+\tthird\n+\tsecond\n+\tfirst\n+\tEOF\n+\tgit -C repo-format log --stdin --no-walk \\\n+\t\t\"$opts\" --format=\"$format\" >expect <input &&\n+\tgit -C repo-format format-rev \"$opts\" \\\n+\t\t--stdin-mode=revs --format=\"$format\" >actual <input &&\n+\ttest_cmp expect actual\n+}\n+\n+format_rev_err_cmp_log () {\n+\topts=\"$1\"\n+\tformat=reference\n+\t# No input since we ought to fail while parsing options\n+\ttest_must_fail git -C repo-format log --stdin --no-walk \\\n+\t\t\"$opts\" --format=\"$format\" 2>expect &&\n+\ttest_must_fail git -C repo-format format-rev \"$opts\" \\\n+\t\t--stdin-mode=revs --format=\"$format\" 2>actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'format-rev --color' '\n+\tformat_rev_cmp_log --color=always &&\n+\tformat_rev_cmp_log --color &&\n+\tformat_rev_cmp_log --no-color &&\n+\tformat_rev_err_cmp_log --color=not-valid\n+'\n+\n+test_expect_success 'format-rev --abbrev' '\n+\tformat_rev_cmp_log --abbrev=31 &&\n+\tformat_rev_cmp_log --no-abbrev\n+'\n+\n+test_expect_success 'format-rev --date' '\n+\tformat_rev_cmp_log --date=relative &&\n+\tformat_rev_cmp_log --date=iso-strict &&\n+\tformat_rev_err_cmp_log --date=not-valid\n+'\n+\n test_done\n-- \n2.54.0.22.g9e26862b904\n\n"},{"id":"550553","messageId":"xmqqfr0hswxm.fsf@gitster.g","threadId":"66168","inReplyTo":"factor_opts_into_struct.b82@msgid.xyz","subject":"Re: [PATCH 2/4] format-rev: factor option variables into a struct","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-13T18:21:57Z","receivedAt":"2026-08-13T18:22:00Z","isPatch":true,"body":"kristofferhaugsbakk@fastmail.com writes:\n\n> From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n>\n> We will in two commits add three more options to this command.\n> Let’s prepare for that by moving option variables into a struct\n> so that we get less local variables.\n>\n> This allows us to inline `format_nul_data` into this new\n> structure. Let’s also rename `stdin_mode_arg` to `stdin_mode`.\n> (We couldn’t use `stdin_mode` before because of the enumeration\n> with the same name.)\n>\n> Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n> ---\n>  builtin/name-rev.c | 44 +++++++++++++++++++++++---------------------\n>  1 file changed, 23 insertions(+), 21 deletions(-)\n>\n> diff --git a/builtin/name-rev.c b/builtin/name-rev.c\n> index 254c88199fd..7d824aa1c5d 100644\n> --- a/builtin/name-rev.c\n> +++ b/builtin/name-rev.c\n> @@ -772,16 +772,19 @@ int cmd_name_rev(int argc,\n>  \treturn 0;\n>  }\n>  \n> -struct format_nul_data {\n> +struct format_rev_data {\n> +\tconst char *format;\n> +\tconst char *stdin_mode;\n>  \tbool nul_input;\n>  \tbool nul_output;\n> +\tstruct string_list notes;\n>  };\n>  \n>  static int format_nul_cb(const struct option *option,\n>  \t\t\t const char *arg,\n>  \t\t\t int unset)\n>  {\n> -\tstruct format_nul_data *data = option->value;\n> +\tstruct format_rev_data *data = option->value;\n>  \tdata->nul_input = 1;\n>  \tdata->nul_output = 1;\n>  \tBUG_ON_OPT_NEG(unset);\n> @@ -813,31 +816,30 @@ int cmd_format_rev(int argc,\n>  \t\t   const char *prefix,\n>  \t\t   struct repository *repo UNUSED)\n>  {\n> -\tconst char *format = NULL;\n> +\tstruct format_rev_data data = {\n> +\t\tNULL, NULL, 0, 0, STRING_LIST_INIT_NODUP\n> +\t};\n\nIt will make it easier to maintain if you used designated\ninitializer here, i.e.,\n\n\tstruct format_rev_data data = {\n\t\t.notes = STRING_LIST_INIT_NODUP,\n\t};\n\nThe other members not explicitly mentioned by the initializer will\nbe zero-initialized.\n\nOther parts of the patch look good.\n\nThanks.\n"},{"id":"550604","messageId":"add02b44-8896-48af-9338-bb913e83ff82@app.fastmail.com","threadId":"66168","inReplyTo":"xmqqfr0hswxm.fsf@gitster.g","subject":"Re: [PATCH 2/4] format-rev: factor option variables into a struct","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-14T10:54:26Z","receivedAt":"2026-08-14T10:54:54Z","isPatch":true,"body":"On Thu, Aug 13, 2026, at 20:21, Junio C Hamano wrote:\n> kristofferhaugsbakk@fastmail.com writes:\n>> From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n>>[snip]\n>> -\tconst char *format = NULL;\n>> +\tstruct format_rev_data data = {\n>> +\t\tNULL, NULL, 0, 0, STRING_LIST_INIT_NODUP\n>> +\t};\n>\n> It will make it easier to maintain if you used designated\n> initializer here, i.e.,\n>\n> \tstruct format_rev_data data = {\n> \t\t.notes = STRING_LIST_INIT_NODUP,\n> \t};\n>\n\nThat is excellent. Thanks!\n\n>[snip]\n"},{"id":"550653","messageId":"xmqqbjb4ktz5.fsf@gitster.g","threadId":"66168","inReplyTo":"format-rev_three_more_opts.b84@msgid.xyz","subject":"Re: [PATCH 4/4] format-rev: learn --abbrev, --color, and --date","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-15T02:17:34Z","receivedAt":"2026-08-15T02:17:37Z","isPatch":true,"body":"kristofferhaugsbakk@fastmail.com writes:\n\n> +static int date_cb(const struct option *option,\n> +\t\t   const char *arg,\n> +\t\t   int unset)\n> +{\n> +\tstruct rev_info *data = option->value;\n> +\tparse_date_format(arg, &data->date_mode);\n> +\tdata->date_mode_explicit = 1;\n> +\tBUG_ON_OPT_NEG(unset);\n> +\treturn 0;\n> +}\n\nThis BUG_ON_OPT_NEG(unset) is a bit curious and confusing to me.  If\nthe caller could pass unset==1 (e.g., \"--no-date\"), option->value\nwould be NULL, and we would already have dereferenced data->date_mode\nwhen preparing to call parse_date_format().\n\nOn the other hand, ...\n\n> +\t\tOPT_CALLBACK_F(0, \"date\", &data.rev, N_(\"date\"),\n> +\t\t\t       N_(\"date format\"),\n> +\t\t\t       PARSE_OPT_NONEG, date_cb),\n\n... because we mark the option entry with PARSE_OPT_NONEG,\n\"--no-date\" would not cause date_cb() to be called with unset==1.\n\nI guess, from existing uses of BUG_ON_OPT_NEG() elsewhere (like\napply.c), that the intention is to notice when this callback\nfunction is broken by future changes, i.e., somebody careless makes\nthe calling parse_options(), or an additional side caller that calls\nthis callback directly, pass unset==1 and option->value==NULL\ncombinations.  But then the assertion should come before the first\npotentially problematic use, i.e., in this order:\n\n\tstruct rev_info *data = option->value;\n\n\tBUG_ON_OPT_NEG(unset);\n\tparse_date_format(arg, &data->date_mode);\n\tdata->date_mode_explicit = 1;\n\treturn 0;\n\nor the assertion will not trigger before the code segfaults, no?\n\nThanks.\n"},{"id":"550704","messageId":"0bd9c642-9e88-4c82-81ee-20fdeb3c2797@app.fastmail.com","threadId":"66168","inReplyTo":"xmqqbjb4ktz5.fsf@gitster.g","subject":"Re: [PATCH 4/4] format-rev: learn --abbrev, --color, and --date","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-17T14:48:54Z","receivedAt":"2026-08-17T14:49:28Z","isPatch":true,"body":"On Sat, Aug 15, 2026, at 04:17, Junio C Hamano wrote:\n> kristofferhaugsbakk@fastmail.com writes:\n>\n>> +static int date_cb(const struct option *option,\n>> +\t\t   const char *arg,\n>> +\t\t   int unset)\n>> +{\n>> +\tstruct rev_info *data = option->value;\n>> +\tparse_date_format(arg, &data->date_mode);\n>> +\tdata->date_mode_explicit = 1;\n>> +\tBUG_ON_OPT_NEG(unset);\n>> +\treturn 0;\n>> +}\n>\n> This BUG_ON_OPT_NEG(unset) is a bit curious and confusing to me.  If\n> the caller could pass unset==1 (e.g., \"--no-date\"), option->value\n> would be NULL, and we would already have dereferenced data->date_mode\n> when preparing to call parse_date_format().\n\nWell spotted.\n\n>\n> On the other hand, ...\n>\n>> +\t\tOPT_CALLBACK_F(0, \"date\", &data.rev, N_(\"date\"),\n>> +\t\t\t       N_(\"date format\"),\n>> +\t\t\t       PARSE_OPT_NONEG, date_cb),\n>\n> ... because we mark the option entry with PARSE_OPT_NONEG,\n> \"--no-date\" would not cause date_cb() to be called with unset==1.\n>\n> I guess, from existing uses of BUG_ON_OPT_NEG() elsewhere (like\n> apply.c), that the intention is to notice when this callback\n> function is broken by future changes, i.e., somebody careless makes\n> the calling parse_options(), or an additional side caller that calls\n> this callback directly, pass unset==1 and option->value==NULL\n> combinations.  But then the assertion should come before the first\n> potentially problematic use, i.e., in this order:\n\nThis is totally a monkey see and moneky do situation. Mirroring\nparse-options flags as `BUG` statements. Down to the outright wrong\nassertion/BUG placement. So this needs to be changed\n\nI have these same statements on the existing callback, for `--null`:\n\n\n    static int format_nul_cb(const struct option *option,\n                             const char *arg,\n                             int unset)\n    {\n            struct format_rev_data *data = option->value;\n            data->nul_input = 1;\n            data->nul_output = 1;\n            BUG_ON_OPT_NEG(unset);\n            BUG_ON_OPT_ARG(arg);\n            return 0;\n    }\n\nBut this does not have the `NULL` deref. problem since we just\nunconditionally set two boolean values. Still, for readability it’s\nbetter for these two statements to go at the start. Since they are\npreconditions. I will add this as a patch/commit to the series.\n\n>\n> \tstruct rev_info *data = option->value;\n>\n> \tBUG_ON_OPT_NEG(unset);\n> \tparse_date_format(arg, &data->date_mode);\n> \tdata->date_mode_explicit = 1;\n> \treturn 0;\n>\n> or the assertion will not trigger before the code segfaults, no?\n"},{"id":"550711","messageId":"xmqqa4qk661p.fsf@gitster.g","threadId":"66168","inReplyTo":"0bd9c642-9e88-4c82-81ee-20fdeb3c2797@app.fastmail.com","subject":"Re: [PATCH 4/4] format-rev: learn --abbrev, --color, and --date","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-17T16:54:58Z","receivedAt":"2026-08-17T16:55:03Z","isPatch":true,"body":"\"Kristoffer Haugsbakk\" <kristofferhaugsbakk@fastmail.com> writes:\n\n>     static int format_nul_cb(const struct option *option,\n>                              const char *arg,\n>                              int unset)\n>     {\n>             struct format_rev_data *data = option->value;\n>             data->nul_input = 1;\n>             data->nul_output = 1;\n>             BUG_ON_OPT_NEG(unset);\n>             BUG_ON_OPT_ARG(arg);\n>             return 0;\n>     }\n>\n> But this does not have the `NULL` deref. problem since we just\n> unconditionally set two boolean values. Still, for readability it’s\n> better for these two statements to go at the start. Since they are\n> preconditions. I will add this as a patch/commit to the series.\n\nYeah, it would be a good idea to establish the \"validate before\ndoing anything with the parameters\" pattern.\n\nThanks.\n"},{"id":"550731","messageId":"3a55c58f-1ada-414c-a35d-40590c635b82@app.fastmail.com","threadId":"66168","inReplyTo":"format-rev_three_more_opts.b84@msgid.xyz","subject":"Re: [PATCH 4/4] format-rev: learn --abbrev, --color, and --date","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-18T05:11:17Z","receivedAt":"2026-08-18T05:11:43Z","isPatch":true,"body":"On Thu, Aug 13, 2026, at 19:24, kristofferhaugsbakk@fastmail.com wrote:\n>[snip]\n> +static int date_cb(const struct option *option,\n> +\t\t   const char *arg,\n> +\t\t   int unset)\n> +{\n> +\tstruct rev_info *data = option->value;\n> +\tparse_date_format(arg, &data->date_mode);\n\nThe documentation for this function says that we need to call a release\nfunction in case a custom format was used. That is currently missing.\n\n> +\tdata->date_mode_explicit = 1;\n> +\tBUG_ON_OPT_NEG(unset);\n> +\treturn 0;\n> +}\n> +\n>[snip]\n"},{"id":"550738","messageId":"V2_CV_format-rev_three_more_opts.bd3@msgid.xyz","threadId":"66168","inReplyTo":"CV_format-rev_three_more_opts.b80@msgid.xyz","subject":"[PATCH v2 0/5] format-rev: add --abbrev, --color, and --date","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-18T09:57:29Z","receivedAt":"2026-08-18T09:57:51Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nTopic name (applied): kh/format-rev-more-options\n\nTopic summary: Add three more options for controlling the formatting. Also\ndo some minor refactoring and text fixes as preparatory steps.\n\n§ Changes in v2\n\nSee the patch notes for details.\n\n• Use designated initializer syntax. That’s more readable since you pair\n  the field with the value and you can omit zero-value fields.\n\n  https://lore.kernel.org/git/xmqqfr0hswxm.fsf@gitster.g/\n• Fix useless `BUG` placements https://lore.kernel.org/git/xmqqfr0hswxm.fsf@gitster.g/\n• Add preliminary patch “place BUG calls first in callback” for existing\n  `BUG` statement placement\n  • Based on the previous point\n• Patch “learn --abbrev, --color, and --date”: test a few more options\n\n[1/5] format-rev: use lower case for opts description\n[2/5] format-rev: place BUG calls first in callback\n[3/5] format-rev: factor option variables into a struct\n[4/5] doc: rev-list-options.adoc: factor out --date alts\n[5/5] format-rev: learn --abbrev, --color, and --date\n\n Documentation/git-format-rev.adoc             | 44 ++++++++-\n .../rev-list-option-date-alternatives.adoc    | 55 +++++++++++\n Documentation/rev-list-options.adoc           | 56 +----------\n builtin/name-rev.c                            | 92 ++++++++++++-------\n t/t6120-describe.sh                           | 58 ++++++++++++\n 5 files changed, 212 insertions(+), 93 deletions(-)\n create mode 100644 Documentation/rev-list-option-date-alternatives.adoc\n\nInterdiff against v1:\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 0c9014ca594..fa20a2774be 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -788,10 +788,10 @@ static int format_nul_cb(const struct option *option,\n \t\t\t int unset)\n {\n \tstruct format_rev_data *data = option->value;\n-\tdata->nul_input = 1;\n-\tdata->nul_output = 1;\n \tBUG_ON_OPT_NEG(unset);\n \tBUG_ON_OPT_ARG(arg);\n+\tdata->nul_input = 1;\n+\tdata->nul_output = 1;\n \treturn 0;\n }\n \n@@ -800,9 +800,9 @@ static int date_cb(const struct option *option,\n \t\t   int unset)\n {\n \tstruct rev_info *data = option->value;\n+\tBUG_ON_OPT_NEG(unset);\n \tparse_date_format(arg, &data->date_mode);\n \tdata->date_mode_explicit = 1;\n-\tBUG_ON_OPT_NEG(unset);\n \treturn 0;\n }\n \n@@ -830,8 +830,9 @@ int cmd_format_rev(int argc,\n \t\t   struct repository *repo UNUSED)\n {\n \tstruct format_rev_data data = {\n-\t\tNULL, NULL, 0, 0, STRING_LIST_INIT_NODUP,\n-\t\tREV_INFO_INIT, GIT_COLOR_AUTO\n+\t\t.notes = STRING_LIST_INIT_NODUP,\n+\t\t.rev = REV_INFO_INIT,\n+\t\t.color = GIT_COLOR_AUTO,\n \t};\n \tenum stdin_mode stdin_mode;\n \tchar output_terminator;\n@@ -953,6 +954,7 @@ int cmd_format_rev(int argc,\n \t\tBUG(\"uncovered case: %d\", stdin_mode);\n \t}\n \n+\tdate_mode_release(&data.rev.date_mode);\n \tstrbuf_release(&scratch_buf);\n \tstring_list_clear(&data.notes, 0);\n \trelease_display_notes(&format_notes_opt);\ndiff --git a/t/t6120-describe.sh b/t/t6120-describe.sh\nindex 2621edb5937..a15da979abf 100755\n--- a/t/t6120-describe.sh\n+++ b/t/t6120-describe.sh\n@@ -1026,9 +1026,9 @@ format_rev_cmp_log () {\n \tfirst\n \tEOF\n \tgit -C repo-format log --stdin --no-walk \\\n-\t\t\"$opts\" --format=\"$format\" >expect <input &&\n-\tgit -C repo-format format-rev \"$opts\" \\\n-\t\t--stdin-mode=revs --format=\"$format\" >actual <input &&\n+\t\t--format=\"$format\" \"$opts\" >expect <input &&\n+\tgit -C repo-format format-rev --stdin-mode=revs \\\n+\t\t--format=\"$format\" \"$opts\" >actual <input &&\n \ttest_cmp expect actual\n }\n \n@@ -1037,9 +1037,9 @@ format_rev_err_cmp_log () {\n \tformat=reference\n \t# No input since we ought to fail while parsing options\n \ttest_must_fail git -C repo-format log --stdin --no-walk \\\n-\t\t\"$opts\" --format=\"$format\" 2>expect &&\n-\ttest_must_fail git -C repo-format format-rev \"$opts\" \\\n-\t\t--stdin-mode=revs --format=\"$format\" 2>actual &&\n+\t\t--format=\"$format\" \"$opts\" 2>expect &&\n+\ttest_must_fail git -C repo-format format-rev \\\n+\t\t--stdin-mode=revs --format=\"$format\" \"$opts\" 2>actual &&\n \ttest_cmp expect actual\n }\n \n@@ -1051,6 +1051,7 @@ test_expect_success 'format-rev --color' '\n '\n \n test_expect_success 'format-rev --abbrev' '\n+\tformat_rev_cmp_log --abbrev &&\n \tformat_rev_cmp_log --abbrev=31 &&\n \tformat_rev_cmp_log --no-abbrev\n '\n@@ -1058,7 +1059,20 @@ test_expect_success 'format-rev --abbrev' '\n test_expect_success 'format-rev --date' '\n \tformat_rev_cmp_log --date=relative &&\n \tformat_rev_cmp_log --date=iso-strict &&\n-\tformat_rev_err_cmp_log --date=not-valid\n+\t# This also tests the only case where we need to release\n+\t# the data for the parsed format\n+\tformat_rev_cmp_log --date=\"format:%c\" &&\n+\tformat_rev_err_cmp_log --date=not-valid &&\n+\t# Test --date (no arg) next\n+\t# We cannot compare the output to git-log(1)\n+\t# because that command uses a slightly different\n+\t# error message (different library)\n+\tcat >expect <<-EOF &&\n+\terror: option \\`date${SQ} requires a value\n+\tEOF\n+\ttest_must_fail git -C repo-format format-rev \\\n+\t\t--stdin-mode=revs --format=\"$format\" --date 2>actual &&\n+\ttest_cmp expect actual\n '\n \n test_done\nRange-diff against v1:\n1:  eb84b1b6341 = 1:  eb84b1b6341 format-rev: use lower case for opts description\n-:  ----------- > 2:  2cb12e3ce48 format-rev: place BUG calls first in callback\n2:  278eb852121 ! 3:  0b653b1d218 format-rev: factor option variables into a struct\n    @@ builtin/name-rev.c: int cmd_name_rev(int argc,\n      {\n     -\tstruct format_nul_data *data = option->value;\n     +\tstruct format_rev_data *data = option->value;\n    - \tdata->nul_input = 1;\n    - \tdata->nul_output = 1;\n      \tBUG_ON_OPT_NEG(unset);\n    + \tBUG_ON_OPT_ARG(arg);\n    + \tdata->nul_input = 1;\n     @@ builtin/name-rev.c: int cmd_format_rev(int argc,\n      \t\t   const char *prefix,\n      \t\t   struct repository *repo UNUSED)\n      {\n     -\tconst char *format = NULL;\n     +\tstruct format_rev_data data = {\n    -+\t\tNULL, NULL, 0, 0, STRING_LIST_INIT_NODUP\n    ++\t\t.notes = STRING_LIST_INIT_NODUP,\n     +\t};\n      \tenum stdin_mode stdin_mode;\n     -\tconst char *stdin_mode_arg = NULL;\n3:  cb2cc772b31 = 4:  7556bf04462 doc: rev-list-options.adoc: factor out --date alts\n4:  e6d3e14c692 ! 5:  d1bcad06e24 format-rev: learn --abbrev, --color, and --date\n    @@ builtin/name-rev.c: static int format_nul_cb(const struct option *option,\n     +\t\t   int unset)\n     +{\n     +\tstruct rev_info *data = option->value;\n    ++\tBUG_ON_OPT_NEG(unset);\n     +\tparse_date_format(arg, &data->date_mode);\n     +\tdata->date_mode_explicit = 1;\n    -+\tBUG_ON_OPT_NEG(unset);\n     +\treturn 0;\n     +}\n     +\n    @@ builtin/name-rev.c: static enum stdin_mode parse_stdin_mode(const char *stdin_mo\n      };\n      \n     @@ builtin/name-rev.c: int cmd_format_rev(int argc,\n    - \t\t   struct repository *repo UNUSED)\n      {\n      \tstruct format_rev_data data = {\n    --\t\tNULL, NULL, 0, 0, STRING_LIST_INIT_NODUP\n    -+\t\tNULL, NULL, 0, 0, STRING_LIST_INIT_NODUP,\n    -+\t\tREV_INFO_INIT, GIT_COLOR_AUTO\n    + \t\t.notes = STRING_LIST_INIT_NODUP,\n    ++\t\t.rev = REV_INFO_INIT,\n    ++\t\t.color = GIT_COLOR_AUTO,\n      \t};\n      \tenum stdin_mode stdin_mode;\n      \tchar output_terminator;\n    @@ builtin/name-rev.c: int cmd_format_rev(int argc,\n      \n      \tuserformat_find_requirements(data.format,\n      \t\t\t\t     &format_pp.want);\n    +@@ builtin/name-rev.c: int cmd_format_rev(int argc,\n    + \t\tBUG(\"uncovered case: %d\", stdin_mode);\n    + \t}\n    + \n    ++\tdate_mode_release(&data.rev.date_mode);\n    + \tstrbuf_release(&scratch_buf);\n    + \tstring_list_clear(&data.notes, 0);\n    + \trelease_display_notes(&format_notes_opt);\n     \n      ## t/t6120-describe.sh ##\n     @@ t/t6120-describe.sh: do\n    @@ t/t6120-describe.sh: do\n     +\tfirst\n     +\tEOF\n     +\tgit -C repo-format log --stdin --no-walk \\\n    -+\t\t\"$opts\" --format=\"$format\" >expect <input &&\n    -+\tgit -C repo-format format-rev \"$opts\" \\\n    -+\t\t--stdin-mode=revs --format=\"$format\" >actual <input &&\n    ++\t\t--format=\"$format\" \"$opts\" >expect <input &&\n    ++\tgit -C repo-format format-rev --stdin-mode=revs \\\n    ++\t\t--format=\"$format\" \"$opts\" >actual <input &&\n     +\ttest_cmp expect actual\n     +}\n     +\n    @@ t/t6120-describe.sh: do\n     +\tformat=reference\n     +\t# No input since we ought to fail while parsing options\n     +\ttest_must_fail git -C repo-format log --stdin --no-walk \\\n    -+\t\t\"$opts\" --format=\"$format\" 2>expect &&\n    -+\ttest_must_fail git -C repo-format format-rev \"$opts\" \\\n    -+\t\t--stdin-mode=revs --format=\"$format\" 2>actual &&\n    ++\t\t--format=\"$format\" \"$opts\" 2>expect &&\n    ++\ttest_must_fail git -C repo-format format-rev \\\n    ++\t\t--stdin-mode=revs --format=\"$format\" \"$opts\" 2>actual &&\n     +\ttest_cmp expect actual\n     +}\n     +\n    @@ t/t6120-describe.sh: do\n     +'\n     +\n     +test_expect_success 'format-rev --abbrev' '\n    ++\tformat_rev_cmp_log --abbrev &&\n     +\tformat_rev_cmp_log --abbrev=31 &&\n     +\tformat_rev_cmp_log --no-abbrev\n     +'\n    @@ t/t6120-describe.sh: do\n     +test_expect_success 'format-rev --date' '\n     +\tformat_rev_cmp_log --date=relative &&\n     +\tformat_rev_cmp_log --date=iso-strict &&\n    -+\tformat_rev_err_cmp_log --date=not-valid\n    ++\t# This also tests the only case where we need to release\n    ++\t# the data for the parsed format\n    ++\tformat_rev_cmp_log --date=\"format:%c\" &&\n    ++\tformat_rev_err_cmp_log --date=not-valid &&\n    ++\t# Test --date (no arg) next\n    ++\t# We cannot compare the output to git-log(1)\n    ++\t# because that command uses a slightly different\n    ++\t# error message (different library)\n    ++\tcat >expect <<-EOF &&\n    ++\terror: option \\`date${SQ} requires a value\n    ++\tEOF\n    ++\ttest_must_fail git -C repo-format format-rev \\\n    ++\t\t--stdin-mode=revs --format=\"$format\" --date 2>actual &&\n    ++\ttest_cmp expect actual\n     +'\n     +\n      test_done\n\nbase-commit: 010afd3166ddc64c9863b1506f12cbcdda0d4ea1\n-- \n2.55.0.13.g85d2d65e389\n\n"},{"id":"550739","messageId":"V2_lower_case_opts_descrps.bd4@msgid.xyz","threadId":"66168","inReplyTo":"V2_CV_format-rev_three_more_opts.bd3@msgid.xyz","subject":"[PATCH v2 1/5] format-rev: use lower case for opts description","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-18T09:57:30Z","receivedAt":"2026-08-18T09:58:09Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe option descriptions use a mix of initial capital and lower case\nletters. Lower case is the correct style.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n builtin/name-rev.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 60cbbfb4b7d..254c88199fd 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -833,12 +833,12 @@ int cmd_format_rev(int argc,\n \t\tOPT_STRING_LIST(0, \"notes\", &notes, N_(\"notes\"),\n \t\t\t\tN_(\"display notes for pretty format\")),\n \t\tOPT_CALLBACK_F('z', \"null\", &nul_data, N_(\"z\"),\n-\t\t\t       N_(\"Use NUL for input and output termination\"),\n+\t\t\t       N_(\"use NUL for input and output termination\"),\n \t\t\t       PARSE_OPT_NOARG | PARSE_OPT_NONEG, format_nul_cb),\n \t\tOPT_BOOL(0, \"null-input\", &nul_data.nul_input,\n-\t\t\t N_(\"Use NUL for input termination\")),\n+\t\t\t N_(\"use NUL for input termination\")),\n \t\tOPT_BOOL(0, \"null-output\", &nul_data.nul_output,\n-\t\t\t N_(\"Use NUL for output termination\")),\n+\t\t\t N_(\"use NUL for output termination\")),\n \t\tOPT_END(),\n \t};\n \n-- \n2.55.0.13.g85d2d65e389\n\n"},{"id":"550740","messageId":"V2_BUG_preconditions.bd5@msgid.xyz","threadId":"66168","inReplyTo":"V2_CV_format-rev_three_more_opts.bd3@msgid.xyz","subject":"[PATCH v2 2/5] format-rev: place BUG calls first in callback","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-18T09:57:31Z","receivedAt":"2026-08-18T09:58:27Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nI added these parse-options `BUG` statements based on existing examples;\none `BUG` check per flag. Now, of course the code as-is will not call\nthis callback with `unset` set to `0`, or with an argument\nstring. Rather, these preconditions defend against `opts[]` getting\nchanged *without* changing this callback.\n\nAnd I copied the existing examples that I found down to\nthe placement. And the placement doesn’t matter here; we just\nunconditionally set two variables. Failing on `BUG` before or after\nthat makes no difference to the user. Still, it is better style to\ntest function preconditions as early as possible. So let’s move them\nto the start.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v2: • [new]\n        • https://lore.kernel.org/git/0bd9c642-9e88-4c82-81ee-20fdeb3c2797@app.fastmail.com/\n\n builtin/name-rev.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 254c88199fd..d6686bbdbb9 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -782,10 +782,10 @@ static int format_nul_cb(const struct option *option,\n \t\t\t int unset)\n {\n \tstruct format_nul_data *data = option->value;\n-\tdata->nul_input = 1;\n-\tdata->nul_output = 1;\n \tBUG_ON_OPT_NEG(unset);\n \tBUG_ON_OPT_ARG(arg);\n+\tdata->nul_input = 1;\n+\tdata->nul_output = 1;\n \treturn 0;\n }\n \n-- \n2.55.0.13.g85d2d65e389\n\n"},{"id":"550741","messageId":"V2_factor_opts_into_struct.bd6@msgid.xyz","threadId":"66168","inReplyTo":"V2_CV_format-rev_three_more_opts.bd3@msgid.xyz","subject":"[PATCH v2 3/5] format-rev: factor option variables into a struct","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-18T09:57:32Z","receivedAt":"2026-08-18T09:58:46Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWe will in two commits add three more options to this command.\nLet’s prepare for that by moving option variables into a struct\nso that we get less local variables.\n\nThis allows us to inline `format_nul_data` into this new\nstructure. Let’s also rename `stdin_mode_arg` to `stdin_mode`.\n(We couldn’t use `stdin_mode` before because of the enumeration\nwith the same name.)\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v2:\n    • Designated initializers: https://lore.kernel.org/git/xmqqfr0hswxm.fsf@gitster.g/\n\n builtin/name-rev.c | 44 +++++++++++++++++++++++---------------------\n 1 file changed, 23 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex d6686bbdbb9..c8cb2f2d520 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -772,16 +772,19 @@ int cmd_name_rev(int argc,\n \treturn 0;\n }\n \n-struct format_nul_data {\n+struct format_rev_data {\n+\tconst char *format;\n+\tconst char *stdin_mode;\n \tbool nul_input;\n \tbool nul_output;\n+\tstruct string_list notes;\n };\n \n static int format_nul_cb(const struct option *option,\n \t\t\t const char *arg,\n \t\t\t int unset)\n {\n-\tstruct format_nul_data *data = option->value;\n+\tstruct format_rev_data *data = option->value;\n \tBUG_ON_OPT_NEG(unset);\n \tBUG_ON_OPT_ARG(arg);\n \tdata->nul_input = 1;\n@@ -813,31 +816,30 @@ int cmd_format_rev(int argc,\n \t\t   const char *prefix,\n \t\t   struct repository *repo UNUSED)\n {\n-\tconst char *format = NULL;\n+\tstruct format_rev_data data = {\n+\t\t.notes = STRING_LIST_INIT_NODUP,\n+\t};\n \tenum stdin_mode stdin_mode;\n-\tconst char *stdin_mode_arg = NULL;\n-\tstruct format_nul_data nul_data = { 0, 0 };\n \tchar output_terminator;\n \tstrbuf_getline_fn getline_fn;\n \tstruct display_notes_opt format_notes_opt;\n \tstruct rev_info format_rev = REV_INFO_INIT;\n \tstruct pretty_format format_pp = { 0 };\n-\tstruct string_list notes = STRING_LIST_INIT_NODUP;\n \tstruct strbuf scratch_buf = STRBUF_INIT;\n \tstruct command cmd;\n \tstruct option opts[] = {\n-\t\tOPT_STRING(0, \"format\", &format, N_(\"format\"),\n+\t\tOPT_STRING(0, \"format\", &data.format, N_(\"format\"),\n \t\t\t   N_(\"pretty format to use\")),\n-\t\tOPT_STRING(0, \"stdin-mode\", &stdin_mode_arg, N_(\"stdin-mode\"),\n+\t\tOPT_STRING(0, \"stdin-mode\", &data.stdin_mode, N_(\"stdin-mode\"),\n \t\t\t   N_(\"how revs are processed\")),\n-\t\tOPT_STRING_LIST(0, \"notes\", &notes, N_(\"notes\"),\n+\t\tOPT_STRING_LIST(0, \"notes\", &data.notes, N_(\"notes\"),\n \t\t\t\tN_(\"display notes for pretty format\")),\n-\t\tOPT_CALLBACK_F('z', \"null\", &nul_data, N_(\"z\"),\n+\t\tOPT_CALLBACK_F('z', \"null\", &data, N_(\"z\"),\n \t\t\t       N_(\"use NUL for input and output termination\"),\n \t\t\t       PARSE_OPT_NOARG | PARSE_OPT_NONEG, format_nul_cb),\n-\t\tOPT_BOOL(0, \"null-input\", &nul_data.nul_input,\n+\t\tOPT_BOOL(0, \"null-input\", &data.nul_input,\n \t\t\t N_(\"use NUL for input termination\")),\n-\t\tOPT_BOOL(0, \"null-output\", &nul_data.nul_output,\n+\t\tOPT_BOOL(0, \"null-output\", &data.nul_output,\n \t\t\t N_(\"use NUL for output termination\")),\n \t\tOPT_END(),\n \t};\n@@ -849,18 +851,18 @@ int cmd_format_rev(int argc,\n \t\tusage_with_options(format_rev_usage, opts);\n \t}\n \n-\tif (!format)\n+\tif (!data.format)\n \t\tdie(_(\"'%s' is required\"), \"--format\");\n-\tif (!stdin_mode_arg)\n+\tif (!data.stdin_mode)\n \t\tdie(_(\"'%s' is required\"), \"--stdin-mode\");\n \n-\tgetline_fn = nul_data.nul_input ? strbuf_getline_nul : strbuf_getline_lf;\n-\toutput_terminator = nul_data.nul_output ? '\\0' : '\\n';\n+\tgetline_fn = data.nul_input ? strbuf_getline_nul : strbuf_getline_lf;\n+\toutput_terminator = data.nul_output ? '\\0' : '\\n';\n \n \tinit_display_notes(&format_notes_opt);\n-\tstdin_mode = parse_stdin_mode(stdin_mode_arg);\n+\tstdin_mode = parse_stdin_mode(data.stdin_mode);\n \n-\tget_commit_format(format, &format_rev);\n+\tget_commit_format(data.format, &format_rev);\n \tformat_pp.ctx.rev = &format_rev;\n \tformat_pp.ctx.fmt = format_rev.commit_format;\n \tformat_pp.ctx.abbrev = format_rev.abbrev;\n@@ -868,13 +870,13 @@ int cmd_format_rev(int argc,\n \tformat_pp.ctx.date_mode = format_rev.date_mode;\n \tformat_pp.ctx.color = GIT_COLOR_AUTO;\n \n-\tuserformat_find_requirements(format,\n+\tuserformat_find_requirements(data.format,\n \t\t\t\t     &format_pp.want);\n \tif (format_pp.want.notes) {\n \t\tint ignore_show_notes = 0;\n \t\tstruct string_list_item *n;\n \n-\t\tfor_each_string_list_item(n, &notes)\n+\t\tfor_each_string_list_item(n, &data.notes)\n \t\t\tenable_ref_display_notes(&format_notes_opt,\n \t\t\t\t\t\t &ignore_show_notes,\n \t\t\t\t\t\t n->string);\n@@ -934,7 +936,7 @@ int cmd_format_rev(int argc,\n \t}\n \n \tstrbuf_release(&scratch_buf);\n-\tstring_list_clear(&notes, 0);\n+\tstring_list_clear(&data.notes, 0);\n \trelease_display_notes(&format_notes_opt);\n \treturn 0;\n }\n-- \n2.55.0.13.g85d2d65e389\n\n"},{"id":"550742","messageId":"V2_doc_--date_alts.bd7@msgid.xyz","threadId":"66168","inReplyTo":"V2_CV_format-rev_three_more_opts.bd3@msgid.xyz","subject":"[PATCH v2 4/5] doc: rev-list-options.adoc: factor out --date alts","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-18T09:57:33Z","receivedAt":"2026-08-18T09:59:05Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWe will introduce `--date` to git-format-rev(1) in the next commit and\nwill need to add it to the documentation. Let’s factor out the option\nalternatives so that it can be included in git-format-rev(1).\n\nThe initial paragraph of this option mentions things like git-log(1).\nWe could make it fit in git-format-rev(1) while not changing it for\ngit-rev-list(1) and related commands with some conditionals like\n`ifndef`, but writing a dedicated paragraph is simple enough.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n .../rev-list-option-date-alternatives.adoc    | 55 ++++++++++++++++++\n Documentation/rev-list-options.adoc           | 56 +------------------\n 2 files changed, 56 insertions(+), 55 deletions(-)\n create mode 100644 Documentation/rev-list-option-date-alternatives.adoc\n\ndiff --git a/Documentation/rev-list-option-date-alternatives.adoc b/Documentation/rev-list-option-date-alternatives.adoc\nnew file mode 100644\nindex 00000000000..141570b1059\n--- /dev/null\n+++ b/Documentation/rev-list-option-date-alternatives.adoc\n@@ -0,0 +1,55 @@\n+--\n+`--date=relative` shows dates relative to the current time,\n+e.g. ``2 hours ago''. The `-local` option has no effect for\n+`--date=relative`.\n+\n+`--date=local` is an alias for `--date=default-local`.\n+\n+`--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.\n+The differences to the strict ISO 8601 format are:\n+\n+\t- a space instead of the `T` date/time delimiter\n+\t- a space between time and time zone\n+\t- no colon between hours and minutes of the time zone\n+\n+`--date=iso-strict` (or `--date=iso8601-strict`) shows timestamps in strict\n+ISO 8601 format.\n+\n+`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822\n+format, often found in email messages.\n+\n+`--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.\n+\n+`--date=raw` shows the date as seconds since the epoch (1970-01-01\n+00:00:00 UTC), followed by a space, and then the timezone as an offset\n+from UTC (a `+` or `-` with four digits; the first two are hours, and\n+the second two are minutes). I.e., as if the timestamp were formatted\n+with `strftime(\"%s %z\")`).\n+Note that the `-local` option does not affect the seconds-since-epoch\n+value (which is always measured in UTC), but does switch the accompanying\n+timezone value.\n+\n+`--date=human` shows the timezone if the timezone does not match the\n+current time-zone, and doesn't print the whole date if that matches\n+(ie skip printing year for dates that are \"this year\", but also skip\n+the whole date itself if it's in the last few days and we can just say\n+what weekday it was).  For older dates the hour and minute is also\n+omitted.\n+\n+`--date=unix` shows the date as a Unix epoch timestamp (seconds since\n+1970).  As with `--raw`, this is always in UTC and therefore `-local`\n+has no effect.\n+\n+`--date=format:<format>` feeds the _<format>_ to your system `strftime`,\n+except for `%s`, `%z`, and `%Z`, which are handled internally.\n+Use `--date=format:%c` to show the date in your system locale's\n+preferred format.  See the `strftime`(3) manual for a complete list of\n+format placeholders. When using `-local`, the correct syntax is\n+`--date=format-local:<format>`.\n+\n+`--date=default` is the default format, and is based on ctime(3)\n+output.  It shows a single line with three-letter day of the week,\n+three-letter month, day-of-month, hour-minute-seconds in \"HH:MM:SS\"\n+format, followed by 4-digit year, plus timezone information, unless\n+the local time zone is used, e.g. `Thu Jan 1 00:00:00 1970 +0000`.\n+--\ndiff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc\nindex fd831f0ec64..6e6093f4747 100644\n--- a/Documentation/rev-list-options.adoc\n+++ b/Documentation/rev-list-options.adoc\n@@ -1132,61 +1132,7 @@ include::pretty-options.adoc[]\n \tauthor's). If `-local` is appended to the format (e.g.,\n \t`iso-local`), the user's local time zone is used instead.\n +\n---\n-`--date=relative` shows dates relative to the current time,\n-e.g. ``2 hours ago''. The `-local` option has no effect for\n-`--date=relative`.\n-\n-`--date=local` is an alias for `--date=default-local`.\n-\n-`--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.\n-The differences to the strict ISO 8601 format are:\n-\n-\t- a space instead of the `T` date/time delimiter\n-\t- a space between time and time zone\n-\t- no colon between hours and minutes of the time zone\n-\n-`--date=iso-strict` (or `--date=iso8601-strict`) shows timestamps in strict\n-ISO 8601 format.\n-\n-`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822\n-format, often found in email messages.\n-\n-`--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.\n-\n-`--date=raw` shows the date as seconds since the epoch (1970-01-01\n-00:00:00 UTC), followed by a space, and then the timezone as an offset\n-from UTC (a `+` or `-` with four digits; the first two are hours, and\n-the second two are minutes). I.e., as if the timestamp were formatted\n-with `strftime(\"%s %z\")`).\n-Note that the `-local` option does not affect the seconds-since-epoch\n-value (which is always measured in UTC), but does switch the accompanying\n-timezone value.\n-\n-`--date=human` shows the timezone if the timezone does not match the\n-current time-zone, and doesn't print the whole date if that matches\n-(ie skip printing year for dates that are \"this year\", but also skip\n-the whole date itself if it's in the last few days and we can just say\n-what weekday it was).  For older dates the hour and minute is also\n-omitted.\n-\n-`--date=unix` shows the date as a Unix epoch timestamp (seconds since\n-1970).  As with `--raw`, this is always in UTC and therefore `-local`\n-has no effect.\n-\n-`--date=format:<format>` feeds the _<format>_ to your system `strftime`,\n-except for `%s`, `%z`, and `%Z`, which are handled internally.\n-Use `--date=format:%c` to show the date in your system locale's\n-preferred format.  See the `strftime`(3) manual for a complete list of\n-format placeholders. When using `-local`, the correct syntax is\n-`--date=format-local:<format>`.\n-\n-`--date=default` is the default format, and is based on ctime(3)\n-output.  It shows a single line with three-letter day of the week,\n-three-letter month, day-of-month, hour-minute-seconds in \"HH:MM:SS\"\n-format, followed by 4-digit year, plus timezone information, unless\n-the local time zone is used, e.g. `Thu Jan 1 00:00:00 1970 +0000`.\n---\n+include::rev-list-option-date-alternatives.adoc[]\n \n ifdef::git-rev-list[]\n `--header`::\n-- \n2.55.0.13.g85d2d65e389\n\n"},{"id":"550743","messageId":"V2_format-rev_three_more_opts.bd8@msgid.xyz","threadId":"66168","inReplyTo":"V2_CV_format-rev_three_more_opts.bd3@msgid.xyz","subject":"[PATCH v2 5/5] format-rev: learn --abbrev, --color, and --date","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-08-18T09:57:34Z","receivedAt":"2026-08-18T09:59:23Z","isPatch":true,"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nAdd three more options for controlling the formatting.\n\nThis does not complete all the pretty formatting knobs for this command\nrelative to e.g. git-log(1), but it does add the most important ones, in\nmy opinion. We can see which are missing by taking a look at\n`Documentation/pretty-options.adoc`:\n\n• `--encoding=<encoding>`\n• `--show-signature`\n• `--expand-tabs=<n>`\n\n***\n\nWe could add these options to the command synopsis, but let’s instead\nsimplify the synopsis to just mention the mandatory options and stuff\nthe other ones into `[<options>]`. I don’t think a long command synopsis\nline is useful. And this way the two mandatory options stand out more.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v2:\n    • Designated initializers: https://lore.kernel.org/git/xmqqfr0hswxm.fsf@gitster.g/\n    • Fix useless `BUG` placements https://lore.kernel.org/git/xmqqfr0hswxm.fsf@gitster.g/\n    • Add a few more tests for different option arguments (or no args)\n    • And the new tests revealed that I needed to change the helper\n      functions so that the option (`opts`) goes last. Or else we\n      couldn’t test bare `--date` (no arg, error) because of\n      inconsistent ordering between log/format-rev and this:\n    \n          $ git format-rev --date --stdin-mode=revs\n          fatal: unknown date format --stdin-mode=revs\n    • Release `date_mode`: https://lore.kernel.org/git/3a55c58f-1ada-414c-a35d-40590c635b82@app.fastmail.com/\n    \n      I wondered if I would need a `goto cleanup` in order to deal with cases\n      like this:\n    \n          ... --date=format:%c --format\n    \n      In other words, the command fails because we are missing an\n      argument to `--format` but we have at that point already called\n      the parse function. But my leakcheck setup didn’t call out any\n      errors:\n    \n          CC = clang\n          SANITIZE = address\n          CFLAGS = -O1 -g3 -fno-omit-frame-pointer\n          NO_GETTEXT = 1\n    ---\n    v1:\n    > We can see which are missing by taking a look at\n    \n    Or am I missing some?\n\n Documentation/git-format-rev.adoc | 44 +++++++++++++++++++++--\n builtin/name-rev.c                | 42 ++++++++++++++++------\n t/t6120-describe.sh               | 58 +++++++++++++++++++++++++++++++\n 3 files changed, 130 insertions(+), 14 deletions(-)\n\ndiff --git a/Documentation/git-format-rev.adoc b/Documentation/git-format-rev.adoc\nindex 505a52feccd..1a06ccbf9b8 100644\n--- a/Documentation/git-format-rev.adoc\n+++ b/Documentation/git-format-rev.adoc\n@@ -9,7 +9,7 @@ git-format-rev - EXPERIMENTAL: Pretty format revisions on demand\n SYNOPSIS\n --------\n [synopsis]\n-(EXPERIMENTAL!) git format-rev --stdin-mode=<mode> --format=<pretty> [--[no-]notes=<ref>] [-z] [--[no-]null-output] [--[no-]null-input]\n+(EXPERIMENTAL!) git format-rev [<options>] --stdin-mode=<mode> --format=<pretty>\n \n DESCRIPTION\n -----------\n@@ -33,8 +33,8 @@ OPTIONS\n The argument `rev` is also accepted.\n \n `text`;; Formats all commit object names found in freeform text. These\n-\tmust be full object names, i.e. abbreviated hexadecimal object\n-\tnames will not be interpreted.\n+\tmust be full object names, i.e. abbreviated hexadecimal (_hex_)\n+\tobject names will not be interpreted.\n +\n Anything that is parsed as an object name but that is not found to be a\n commit object name is left alone (echoed).\n@@ -76,6 +76,44 @@ This is useful if the output could contain newlines, for example if the\n +\n This is useful if the input revision expressions could contain newlines.\n \n+`--color[=<when>]`::\n+`--no-color`::\n+\tRespect color formatting. The default color behavior is\n+\t`auto`. Bare `--color` is the same as `--color=always`.\n++\n+Giving `--no-color` is the same as `--color=never`.\n++\n+_<when>_ must be one of:\n++\n+--\n+`always`;;\n+\tAlways use color, even if the output is something like a file.\n+`never`;;\n+\tNever use color.\n+`auto`;;\n+\tUse color when the output is a terminal but not when the output\n+\tis something like a file.\n+--\n+\n+`--abbrev[=<n>]`::\n+`--no-abbrev`::\n+\tAbbreviate the commit hex output. Without _<n>_ it will find the\n+\tminimum length which can describe the commit uniquely, with some\n+\textra slack. Giving _<n>_ specifies the minimum length; a longer\n+\tlength will be used if needed.\n++\n+Giving `--no-abbrev` will turn off abbreviation, showing the full commit\n+hex output.\n++\n+Note that some pretty formats use `--abbrev`. This behavior can be\n+controlled with these two options.\n+\n+`--date=<format>`::\n+\tDate format for pretty formats. Note that date atoms like `%aI`\n+\tare not affected. This option cannot be negated.\n++\n+include::rev-list-option-date-alternatives.adoc[]\n+\n [[io]]\n INPUT AND OUTPUT FORMAT\n -----------------------\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex c8cb2f2d520..fa20a2774be 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -21,6 +21,7 @@\n #include \"revision.h\"\n #include \"notes.h\"\n #include \"write-or-die.h\"\n+#include \"date.h\"\n \n /*\n  * One day.  See the 'name a rev shortly after epoch' test in t6120 when\n@@ -778,6 +779,8 @@ struct format_rev_data {\n \tbool nul_input;\n \tbool nul_output;\n \tstruct string_list notes;\n+\tstruct rev_info rev;\n+\tint color;\n };\n \n static int format_nul_cb(const struct option *option,\n@@ -792,6 +795,17 @@ static int format_nul_cb(const struct option *option,\n \treturn 0;\n }\n \n+static int date_cb(const struct option *option,\n+\t\t   const char *arg,\n+\t\t   int unset)\n+{\n+\tstruct rev_info *data = option->value;\n+\tBUG_ON_OPT_NEG(unset);\n+\tparse_date_format(arg, &data->date_mode);\n+\tdata->date_mode_explicit = 1;\n+\treturn 0;\n+}\n+\n static enum stdin_mode parse_stdin_mode(const char *stdin_mode)\n {\n \tif (!strcmp(stdin_mode, \"text\"))\n@@ -805,9 +819,8 @@ static enum stdin_mode parse_stdin_mode(const char *stdin_mode)\n }\n \n static char const *const format_rev_usage[] = {\n-\tN_(\"(EXPERIMENTAL!) git format-rev --stdin-mode=<mode> \"\n-\t   \"--format=<pretty> [--[no-]notes=<ref>] \"\n-\t   \"[-z] [--[no-]null-output] [--[no-]null-input]\"),\n+\tN_(\"(EXPERIMENTAL!) git format-rev [<options>] \"\n+\t   \"--stdin-mode=<mode> --format=<pretty>\"),\n \tNULL\n };\n \n@@ -818,12 +831,13 @@ int cmd_format_rev(int argc,\n {\n \tstruct format_rev_data data = {\n \t\t.notes = STRING_LIST_INIT_NODUP,\n+\t\t.rev = REV_INFO_INIT,\n+\t\t.color = GIT_COLOR_AUTO,\n \t};\n \tenum stdin_mode stdin_mode;\n \tchar output_terminator;\n \tstrbuf_getline_fn getline_fn;\n \tstruct display_notes_opt format_notes_opt;\n-\tstruct rev_info format_rev = REV_INFO_INIT;\n \tstruct pretty_format format_pp = { 0 };\n \tstruct strbuf scratch_buf = STRBUF_INIT;\n \tstruct command cmd;\n@@ -834,6 +848,11 @@ int cmd_format_rev(int argc,\n \t\t\t   N_(\"how revs are processed\")),\n \t\tOPT_STRING_LIST(0, \"notes\", &data.notes, N_(\"notes\"),\n \t\t\t\tN_(\"display notes for pretty format\")),\n+\t\tOPT__ABBREV(&data.rev.abbrev),\n+\t\tOPT__COLOR(&data.color, N_(\"use colored output\")),\n+\t\tOPT_CALLBACK_F(0, \"date\", &data.rev, N_(\"date\"),\n+\t\t\t       N_(\"date format\"),\n+\t\t\t       PARSE_OPT_NONEG, date_cb),\n \t\tOPT_CALLBACK_F('z', \"null\", &data, N_(\"z\"),\n \t\t\t       N_(\"use NUL for input and output termination\"),\n \t\t\t       PARSE_OPT_NOARG | PARSE_OPT_NONEG, format_nul_cb),\n@@ -862,13 +881,13 @@ int cmd_format_rev(int argc,\n \tinit_display_notes(&format_notes_opt);\n \tstdin_mode = parse_stdin_mode(data.stdin_mode);\n \n-\tget_commit_format(data.format, &format_rev);\n-\tformat_pp.ctx.rev = &format_rev;\n-\tformat_pp.ctx.fmt = format_rev.commit_format;\n-\tformat_pp.ctx.abbrev = format_rev.abbrev;\n-\tformat_pp.ctx.date_mode_explicit = format_rev.date_mode_explicit;\n-\tformat_pp.ctx.date_mode = format_rev.date_mode;\n-\tformat_pp.ctx.color = GIT_COLOR_AUTO;\n+\tget_commit_format(data.format, &data.rev);\n+\tformat_pp.ctx.rev = &data.rev;\n+\tformat_pp.ctx.fmt = data.rev.commit_format;\n+\tformat_pp.ctx.abbrev = data.rev.abbrev;\n+\tformat_pp.ctx.date_mode_explicit = data.rev.date_mode_explicit;\n+\tformat_pp.ctx.date_mode = data.rev.date_mode;\n+\tformat_pp.ctx.color = data.color;\n \n \tuserformat_find_requirements(data.format,\n \t\t\t\t     &format_pp.want);\n@@ -935,6 +954,7 @@ int cmd_format_rev(int argc,\n \t\tBUG(\"uncovered case: %d\", stdin_mode);\n \t}\n \n+\tdate_mode_release(&data.rev.date_mode);\n \tstrbuf_release(&scratch_buf);\n \tstring_list_clear(&data.notes, 0);\n \trelease_display_notes(&format_notes_opt);\ndiff --git a/t/t6120-describe.sh b/t/t6120-describe.sh\nindex 7a7c46658a3..a15da979abf 100755\n--- a/t/t6120-describe.sh\n+++ b/t/t6120-describe.sh\n@@ -1017,4 +1017,62 @@ do\n \t'\n done <stdin-modes\n \n+format_rev_cmp_log () {\n+\topts=\"$1\"\n+\tformat=reference\n+\tcat >input <<-\\EOF &&\n+\tthird\n+\tsecond\n+\tfirst\n+\tEOF\n+\tgit -C repo-format log --stdin --no-walk \\\n+\t\t--format=\"$format\" \"$opts\" >expect <input &&\n+\tgit -C repo-format format-rev --stdin-mode=revs \\\n+\t\t--format=\"$format\" \"$opts\" >actual <input &&\n+\ttest_cmp expect actual\n+}\n+\n+format_rev_err_cmp_log () {\n+\topts=\"$1\"\n+\tformat=reference\n+\t# No input since we ought to fail while parsing options\n+\ttest_must_fail git -C repo-format log --stdin --no-walk \\\n+\t\t--format=\"$format\" \"$opts\" 2>expect &&\n+\ttest_must_fail git -C repo-format format-rev \\\n+\t\t--stdin-mode=revs --format=\"$format\" \"$opts\" 2>actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'format-rev --color' '\n+\tformat_rev_cmp_log --color=always &&\n+\tformat_rev_cmp_log --color &&\n+\tformat_rev_cmp_log --no-color &&\n+\tformat_rev_err_cmp_log --color=not-valid\n+'\n+\n+test_expect_success 'format-rev --abbrev' '\n+\tformat_rev_cmp_log --abbrev &&\n+\tformat_rev_cmp_log --abbrev=31 &&\n+\tformat_rev_cmp_log --no-abbrev\n+'\n+\n+test_expect_success 'format-rev --date' '\n+\tformat_rev_cmp_log --date=relative &&\n+\tformat_rev_cmp_log --date=iso-strict &&\n+\t# This also tests the only case where we need to release\n+\t# the data for the parsed format\n+\tformat_rev_cmp_log --date=\"format:%c\" &&\n+\tformat_rev_err_cmp_log --date=not-valid &&\n+\t# Test --date (no arg) next\n+\t# We cannot compare the output to git-log(1)\n+\t# because that command uses a slightly different\n+\t# error message (different library)\n+\tcat >expect <<-EOF &&\n+\terror: option \\`date${SQ} requires a value\n+\tEOF\n+\ttest_must_fail git -C repo-format format-rev \\\n+\t\t--stdin-mode=revs --format=\"$format\" --date 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.55.0.13.g85d2d65e389\n\n"},{"id":"551314","messageId":"xmqqse40ei5f.fsf@gitster.g","threadId":"66168","inReplyTo":"V2_CV_format-rev_three_more_opts.bd3@msgid.xyz","subject":"Re: [PATCH v2 0/5] format-rev: add --abbrev, --color, and --date","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-26T20:33:32Z","receivedAt":"2026-08-26T20:33:35Z","isPatch":true,"body":"kristofferhaugsbakk@fastmail.com writes:\n\n> From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n>\n> Topic name (applied): kh/format-rev-more-options\n>\n> Topic summary: Add three more options for controlling the formatting. Also\n> do some minor refactoring and text fixes as preparatory steps.\n>\n> § Changes in v2\n>\n> See the patch notes for details.\n>\n> • Use designated initializer syntax. That’s more readable since you pair\n>   the field with the value and you can omit zero-value fields.\n>\n>   https://lore.kernel.org/git/xmqqfr0hswxm.fsf@gitster.g/\n> • Fix useless `BUG` placements https://lore.kernel.org/git/xmqqfr0hswxm.fsf@gitster.g/\n> • Add preliminary patch “place BUG calls first in callback” for existing\n>   `BUG` statement placement\n>   • Based on the previous point\n> • Patch “learn --abbrev, --color, and --date”: test a few more options\n\nNobody is interested in reviewing these patches?  They looked more\nor less OKish to me, but I am not the best person to review this\nseries, as I do not see much point in this experimental command.\nIt would be nice to see reviews from those who are more invested\nin seeing the command work better than I am.\n\nThanks.\n"},{"id":"551877","messageId":"EBCF44E8-9952-454F-A164-0B7CF273FB30@gmail.com","threadId":"66168","inReplyTo":"xmqqse40ei5f.fsf@gitster.g","subject":"Re: [PATCH v2 0/5] format-rev: add --abbrev, --color, and --date","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-09-03T15:07:50Z","receivedAt":"2026-09-03T15:08:03Z","isPatch":true,"body":"\n> Le 26 août 2026 à 16:33, Junio C Hamano <gitster@pobox.com> a écrit :\n> \n> ﻿kristofferhaugsbakk@fastmail.com writes:\n> \n>> From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n>> \n>> Topic name (applied): kh/format-rev-more-options\n>> \n>> Topic summary: Add three more options for controlling the formatting. Also\n>> do some minor refactoring and text fixes as preparatory steps.\n>> \n>> § Changes in v2\n>> \n>> See the patch notes for details.\n>> \n>> • Use designated initializer syntax. That’s more readable since you pair\n>>  the field with the value and you can omit zero-value fields.\n>> \n>>  https://lore.kernel.org/git/xmqqfr0hswxm.fsf@gitster.g/\n>> • Fix useless `BUG` placements https://lore.kernel.org/git/xmqqfr0hswxm.fsf@gitster.g/\n>> • Add preliminary patch “place BUG calls first in callback” for existing\n>>  `BUG` statement placement\n>>  • Based on the previous point\n>> • Patch “learn --abbrev, --color, and --date”: test a few more options\n> \n> Nobody is interested in reviewing these patches?  They looked more\n> or less OKish to me, but I am not the best person to review this\n> series, as I do not see much point in this experimental command.\n> It would be nice to see reviews from those who are more invested\n> in seeing the command work better than I am.\n> \n> Thanks.\n\nReviewing these are on my list. They’ve been there for a while, \nnow, though, and no promises on timing :/ my personal priorities\nshifted rather quickly for the weekend with the recent 1Password\nannouncement ://\n\nPS hopefully this wrapping is better from my mobile device,\nI’m guessing at the width to hard-break as roughly 1.3 screen lines :)"},{"id":"551879","messageId":"ca525be2-2cb0-4f0d-ad41-a6bebcf87e4c@app.fastmail.com","threadId":"66168","inReplyTo":"EBCF44E8-9952-454F-A164-0B7CF273FB30@gmail.com","subject":"Re: [PATCH v2 0/5] format-rev: add --abbrev, --color, and --date","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-09-03T15:22:29Z","receivedAt":"2026-09-03T15:22:53Z","isPatch":true,"body":"On Thu, Sep 3, 2026, at 17:07, Ben Knoble wrote:\n>> Le 26 août 2026 à 16:33, Junio C Hamano <gitster@pobox.com> a écrit :\n>>\n>> ﻿kristofferhaugsbakk@fastmail.com writes:\n>>>[snip]\n>>\n>> Nobody is interested in reviewing these patches?  They looked more\n>> or less OKish to me, but I am not the best person to review this\n>> series, as I do not see much point in this experimental command.\n>> It would be nice to see reviews from those who are more invested\n>> in seeing the command work better than I am.\n>>\n>> Thanks.\n>\n> Reviewing these are on my list. They’ve been there for a while,\n> now, though, and no promises on timing :/ my personal priorities\n> shifted rather quickly for the weekend with the recent 1Password\n> announcement ://\n>\n> PS hopefully this wrapping is better from my mobile device,\n> I’m guessing at the width to hard-break as roughly 1.3 screen lines :)\n\nI appreciate it—thank you!\n"}]}