{"thread":{"id":"63703","subject":"[GSoC][RFC PATCH 0/2] Add refs list subcommand","startedAt":"2025-06-27T07:49:55Z","lastAt":"2025-08-05T16:12:56Z","messageCount":65,"participants":["Meet Soni","Jean-Noël Avila","Junio C Hamano","shejialuo","Jean-Noël AVILA","Patrick Steinhardt","Eric Sunshine","Phillip Wood"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"520795","messageId":"20250627074934.1761897-1-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":null,"subject":"[GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-06-27T07:49:32Z","receivedAt":"2025-06-27T07:49:55Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"This patch series introduces `git refs list` as a modern replacement for\n`git for-each-ref`, as part of an effort to consolidate ref-related\nfunctionality under a unified `git refs` command.\n\nGit's ref-related operations are currently handled by several distinct\ncommands, such as `git show-ref`, `git for-each-ref`, `git update-ref`,\n`git pack-refs`, etc. This distribution has a few practical drawbacks:\n\n- Users need to rely on multiple commands for related tasks involving\n  refs.\n\n- The commands may differ slightly in behavior and option syntax,\n  leading to inconsistency.\n\nWe propose a long-term consolidation effort to bring ref-related\nsubcommands under the umbrella of a single command: `git refs`.\n\nThe implementation of `git refs list` is functionally identical to `git\nfor-each-ref`. It reuses the same internal logic (cmd_for_each_ref) to\nensure complete backward compatibility. The purpose of this patch is not\nto introduce new behavior but to provide an alternate entry point under\nthe consolidated `git refs` namespace.\n\nThe motivation behind this change is twofold:\n\n- Consolidation: Centralizing ref-related operations makes them easier\n  to discover, use, and maintain.\n\n- Evolution: While the initial goal is parity with existing commands,\n  this consolidation allows for careful reconsideration of which\n  features are essential. Over time, we can:\n\n  - Remove legacy or obscure options that are no longer needed.\n  - Add improvements that wouldn't make sense to bolt onto legacy\n    commands.\n  - Offering a more consistent and user-friendly surface.\n\nTo verify backward compatibility, this patch also includes a test\n`t/t1461-refs-list.sh`, which runs the full `t6300-for-each-ref.sh` test\nusing `git refs list`. The test uses ${GIT_REFS_LIST_CMD:-for-each-ref}\nto allow substitution without duplicating tests.\n\nThis patch is deliberately conservative: it introduces no behavioral\nchanges and leaves `for-each-ref` untouched. The goal is to lay\ngroundwork and demonstrate viability of ref consolidation within `git\nrefs`.\n\nGoing forward, I'd like to initiate a discussion on what the ideal\nsurface of `git refs list` should look like. Which options and features\nfrom `for-each-ref` should be carried over? Are there any that are\nobsolete or overly niche? What improvements might be worth considering\nnow that we have a new, consolidated interface?\n\nFeedback on this, especially from those who rely on `for-each-ref` in\nscripts or tooling would be very helpful.\n\n\nMeet Soni (2):\n  builtin/refs: add list subcommand\n  t: add test for git refs list subcommand\n\n Documentation/git-refs.adoc |  95 ++++++++++\n builtin/refs.c              | 110 ++++++++++++\n t/meson.build               |   1 +\n t/t1461-refs-list.sh        |   8 +\n t/t6300-for-each-ref.sh     | 333 ++++++++++++++++++------------------\n 5 files changed, 384 insertions(+), 163 deletions(-)\n create mode 100755 t/t1461-refs-list.sh\n\n\nbase-commit: cb3b40381e1d5ee32dde96521ad7cfd68eb308a6\n-- \n2.34.1\n\n"},{"id":"520796","messageId":"20250627074934.1761897-2-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250627074934.1761897-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH 1/2] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-06-27T07:49:33Z","receivedAt":"2025-06-27T07:50:05Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Git's reference management is distributed across multiple commands. As\npart of an ongoing effort to consolidate and modernize reference\nhandling, introduce a `list` subcommand under the `git refs` umbrella as\na replacement for `git for-each-ref`.\n\nFor now, cmd_refs_list is effectively a copy of cmd_for_each_ref,\nallowing us to reuse the existing filtering and formatting logic while\navoiding any backward compatibility issues. The core implementation\ncontinues to reside in shared modules.\n\nThis duplication is temporary and intentional: future enhancements will\nbe added exclusively to `git refs list`, providing a gradual path\nforward without disrupting existing workflows.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/git-refs.adoc |  95 +++++++++++++++++++++++++++++++\n builtin/refs.c              | 110 ++++++++++++++++++++++++++++++++++++\n 2 files changed, 205 insertions(+)\n\ndiff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\nindex 4d6dc994f9..d8f81eaabd 100644\n--- a/Documentation/git-refs.adoc\n+++ b/Documentation/git-refs.adoc\n@@ -11,6 +11,13 @@ SYNOPSIS\n [synopsis]\n git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n git refs verify [--strict] [--verbose]\n+git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n+\t      [(--sort=<key>)...] [--format=<format>]\n+\t      [--include-root-refs] [ --stdin | <pattern>... ]\n+\t      [--points-at=<object>]\n+\t      [--merged[=<object>]] [--no-merged[=<object>]]\n+\t      [--contains[=<object>]] [--no-contains[=<object>]]\n+\t      [--exclude=<pattern> ...]\n \n DESCRIPTION\n -----------\n@@ -26,6 +33,11 @@ migrate::\n verify::\n \tVerify reference database consistency.\n \n+list::\n+\tList references in the repository with support for filtering, formatting,\n+\tand sorting. This subcommand uses the same core logic as\n+\tlinkgit:git-for-each-ref[1] and offers equivalent functionality.\n+\n OPTIONS\n -------\n \n@@ -57,6 +69,89 @@ The following options are specific to 'git refs verify':\n --verbose::\n \tWhen verifying the reference database consistency, be chatty.\n \n+The following options are specific to 'git refs list':\n+\n+<pattern>...::\n+\tIf one or more patterns are given, only refs are shown that\n+\tmatch against at least one pattern, either using fnmatch(3) or\n+\tliterally, in the latter case matching completely or from the\n+\tbeginning up to a slash.\n+\n+--stdin::\n+\tIf `--stdin` is supplied, then the list of patterns is read from\n+\tstandard input instead of from the argument list.\n+\n+--count=<count>::\n+\tBy default the command shows all refs that match\n+\t`<pattern>`.  This option makes it stop after showing\n+\tthat many refs.\n+\n+--sort=<key>::\n+\tA field name to sort on.  Prefix `-` to sort in\n+\tdescending order of the value.  When unspecified,\n+\t`refname` is used.  You may use the --sort=<key> option\n+\tmultiple times, in which case the last key becomes the primary\n+\tkey.\n+\n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from a ref being shown and\n+\tthe object it points at. In addition, the string literal `%%`\n+\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n+\tthe character with hex code `xx`. For example, `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n++\n+When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n+TAB %(refname)`.\n+\n+--color[=<when>]::\n+\tRespect any colors specified in the `--format` option. The\n+\t`<when>` field must be one of `always`, `never`, or `auto` (if\n+\t`<when>` is absent, behave as if `always` was given).\n+\n+--shell::\n+--perl::\n+--python::\n+--tcl::\n+\tIf given, strings that substitute `%(fieldname)`\n+\tplaceholders are quoted as string literals suitable for\n+\tthe specified host language.  This is meant to produce\n+\ta scriptlet that can directly be `eval`ed.\n+\n+--points-at=<object>::\n+\tOnly list refs which points at the given object.\n+\n+--merged[=<object>]::\n+\tOnly list refs whose tips are reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--no-merged[=<object>]::\n+\tOnly list refs whose tips are not reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--contains[=<object>]::\n+\tOnly list refs which contain the specified commit (HEAD if not\n+\tspecified).\n+\n+--no-contains[=<object>]::\n+\tOnly list refs which don't contain the specified commit (HEAD\n+\tif not specified).\n+\n+--ignore-case::\n+\tSorting and filtering refs are case insensitive.\n+\n+--omit-empty::\n+\tDo not print a newline after formatted refs where the format expands\n+\tto the empty string.\n+\n+--exclude=<pattern>::\n+\tIf one or more patterns are given, only refs which do not match\n+\tany excluded pattern(s) are shown. Matching is done using the\n+\tsame rules as `<pattern>` above.\n+\n+--include-root-refs::\n+\tList root refs (HEAD and pseudorefs) apart from regular refs.\n+\n+\n KNOWN LIMITATIONS\n -----------------\n \ndiff --git a/builtin/refs.c b/builtin/refs.c\nindex 998d2a2c1c..70e1757791 100644\n--- a/builtin/refs.c\n+++ b/builtin/refs.c\n@@ -3,6 +3,7 @@\n #include \"config.h\"\n #include \"fsck.h\"\n #include \"parse-options.h\"\n+#include \"ref-filter.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n #include \"worktree.h\"\n@@ -13,6 +14,15 @@\n #define REFS_VERIFY_USAGE \\\n \tN_(\"git refs verify [--strict] [--verbose]\")\n \n+#define REFS_LIST_USAGE \\\n+\tN_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t   \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n+\t   \"              [--points-at=<object>]\\n\" \\\n+\t   \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t   \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t   \"              [--exclude=<pattern> ...]\")\n+\n static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,\n \t\t\t    struct repository *repo UNUSED)\n {\n@@ -101,6 +111,104 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,\n \treturn ret;\n }\n \n+static int cmd_refs_list(int argc, const char **argv, const char *prefix,\n+\t\t\t struct repository *repo)\n+{\n+\tstruct ref_sorting *sorting;\n+\tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n+\tint icase = 0, include_root_refs = 0, from_stdin = 0;\n+\tstruct ref_filter filter = REF_FILTER_INIT;\n+\tstruct ref_format format = REF_FORMAT_INIT;\n+\tunsigned int flags = FILTER_REFS_REGULAR;\n+\tstruct strvec vec = STRVEC_INIT;\n+\tconst char * const list_usage[] = {\n+\t\tREFS_LIST_USAGE,\n+\t\tNULL\n+\t};\n+\n+\tstruct option opts[] = {\n+\t\tOPT_BIT('s', \"shell\", &format.quote_style,\n+\t\t\tN_(\"quote placeholders suitably for shells\"), QUOTE_SHELL),\n+\t\tOPT_BIT('p', \"perl\",  &format.quote_style,\n+\t\t\tN_(\"quote placeholders suitably for perl\"), QUOTE_PERL),\n+\t\tOPT_BIT(0 , \"python\", &format.quote_style,\n+\t\t\tN_(\"quote placeholders suitably for python\"), QUOTE_PYTHON),\n+\t\tOPT_BIT(0 , \"tcl\",  &format.quote_style,\n+\t\t\tN_(\"quote placeholders suitably for Tcl\"), QUOTE_TCL),\n+\t\tOPT_BOOL(0, \"omit-empty\",  &format.array_opts.omit_empty,\n+\t\t\tN_(\"do not output a newline after empty formatted refs\")),\n+\n+\t\tOPT_GROUP(\"\"),\n+\t\tOPT_INTEGER(0, \"count\", &format.array_opts.max_count, N_(\"show only <n> matched refs\")),\n+\t\tOPT_STRING(0, \"format\", &format.format, N_(\"format\"), N_(\"format to use for the output\")),\n+\t\tOPT__COLOR(&format.use_color, N_(\"respect format colors\")),\n+\t\tOPT_REF_FILTER_EXCLUDE(&filter),\n+\t\tOPT_REF_SORT(&sorting_options),\n+\t\tOPT_CALLBACK(0, \"points-at\", &filter.points_at,\n+\t\t\t     N_(\"object\"), N_(\"print only refs which points at the given object\"),\n+\t\t\t     parse_opt_object_name),\n+\t\tOPT_MERGED(&filter, N_(\"print only refs that are merged\")),\n+\t\tOPT_NO_MERGED(&filter, N_(\"print only refs that are not merged\")),\n+\t\tOPT_CONTAINS(&filter.with_commit, N_(\"print only refs which contain the commit\")),\n+\t\tOPT_NO_CONTAINS(&filter.no_commit, N_(\"print only refs which don't contain the commit\")),\n+\t\tOPT_BOOL(0, \"ignore-case\", &icase, N_(\"sorting and filtering are case insensitive\")),\n+\t\tOPT_BOOL(0, \"stdin\", &from_stdin, N_(\"read reference patterns from stdin\")),\n+\t\tOPT_BOOL(0, \"include-root-refs\", &include_root_refs, N_(\"also include HEAD ref and pseudorefs\")),\n+\t\tOPT_END(),\n+\t};\n+\n+\tformat.format = \"%(objectname) %(objecttype)\\t%(refname)\";\n+\n+\trepo_config(repo, git_default_config, NULL);\n+\n+\t/* Default to sorting by refname unless overridden by --sort= */\n+\tstring_list_append(&sorting_options, \"refname\");\n+\n+\tparse_options(argc, argv, prefix, opts, list_usage, 0);\n+\tif (format.array_opts.max_count < 0) {\n+\t\terror(\"invalid --count value: `%d'\", format.array_opts.max_count);\n+\t\tusage_with_options(list_usage, opts);\n+\t}\n+\tif (HAS_MULTI_BITS(format.quote_style)) {\n+\t\terror(\"more than one quoting style?\");\n+\t\tusage_with_options(list_usage, opts);\n+\t}\n+\tif (verify_ref_format(&format))\n+\t\tusage_with_options(list_usage, opts);\n+\n+\tsorting = ref_sorting_options(&sorting_options);\n+\tref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);\n+\tfilter.ignore_case = icase;\n+\n+\tif (from_stdin) {\n+\t\tstruct strbuf line = STRBUF_INIT;\n+\n+\t\tif (argv[0])\n+\t\t\tdie(_(\"unknown arguments supplied with --stdin\"));\n+\n+\t\twhile (strbuf_getline(&line, stdin) != EOF)\n+\t\t\tstrvec_push(&vec, line.buf);\n+\n+\t\tstrbuf_release(&line);\n+\n+\t\t/* vec.v is NULL-terminated, just like 'argv'. */\n+\t\tfilter.name_patterns = vec.v;\n+\t} else {\n+\t\tfilter.name_patterns = argv;\n+\t}\n+\n+\tif (include_root_refs)\n+\t\tflags |= FILTER_REFS_ROOT_REFS | FILTER_REFS_DETACHED_HEAD;\n+\n+\tfilter.match_as_path = 1;\n+\tfilter_and_format_refs(&filter, flags, sorting, &format);\n+\n+\tref_filter_clear(&filter);\n+\tref_sorting_release(sorting);\n+\tstrvec_clear(&vec);\n+\treturn 0;\n+}\n+\n int cmd_refs(int argc,\n \t     const char **argv,\n \t     const char *prefix,\n@@ -109,12 +217,14 @@ int cmd_refs(int argc,\n \tconst char * const refs_usage[] = {\n \t\tREFS_MIGRATE_USAGE,\n \t\tREFS_VERIFY_USAGE,\n+\t\tREFS_LIST_USAGE,\n \t\tNULL,\n \t};\n \tparse_opt_subcommand_fn *fn = NULL;\n \tstruct option opts[] = {\n \t\tOPT_SUBCOMMAND(\"migrate\", &fn, cmd_refs_migrate),\n \t\tOPT_SUBCOMMAND(\"verify\", &fn, cmd_refs_verify),\n+\t\tOPT_SUBCOMMAND(\"list\", &fn, cmd_refs_list),\n \t\tOPT_END(),\n \t};\n \n-- \n2.34.1\n\n"},{"id":"520797","messageId":"20250627074934.1761897-3-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250627074934.1761897-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH 2/2] t: add test for git refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-06-27T07:49:34Z","receivedAt":"2025-06-27T07:50:17Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Adapt `t/t6300-for-each-ref.sh` to invoke ${GIT_REFS_LIST_CMD},\ndefaulting to `for-each-ref` if the variable is unset.\n\nAdd `t/t1461-refs-list.sh` to test git refs list by setting\n`GIT_REFS_LIST_CMD` to `refs list` and sourcing\n`t/t6300-for-each-ref.sh`\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/meson.build           |   1 +\n t/t1461-refs-list.sh    |   8 +\n t/t6300-for-each-ref.sh | 333 ++++++++++++++++++++--------------------\n 3 files changed, 179 insertions(+), 163 deletions(-)\n create mode 100755 t/t1461-refs-list.sh\n\ndiff --git a/t/meson.build b/t/meson.build\nindex 50e89e764a..c959c039d0 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -224,6 +224,7 @@ integration_tests = [\n   't1450-fsck.sh',\n   't1451-fsck-buffer.sh',\n   't1460-refs-migrate.sh',\n+  't1461-refs-list.sh',\n   't1500-rev-parse.sh',\n   't1501-work-tree.sh',\n   't1502-rev-parse-parseopt.sh',\ndiff --git a/t/t1461-refs-list.sh b/t/t1461-refs-list.sh\nnew file mode 100755\nindex 0000000000..9def7b2e5b\n--- /dev/null\n+++ b/t/t1461-refs-list.sh\n@@ -0,0 +1,8 @@\n+#!/bin/sh\n+\n+test_description='git refs list tests'\n+\n+. ./test-lib.sh\n+\n+GIT_REFS_LIST_CMD='refs list'\n+. \"$TEST_DIRECTORY\"/t6300-for-each-ref.sh\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex ce9af79ab1..74a030371c 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -5,7 +5,9 @@\n \n test_description='for-each-ref test'\n \n-. ./test-lib.sh\n+. \"${TEST_DIRECTORY:-.}/test-lib.sh\"\n+\n+GIT_REFS_LIST_CMD=${GIT_REFS_LIST_CMD:-for-each-ref}\n GNUPGHOME_NOT_USED=$GNUPGHOME\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n . \"$TEST_DIRECTORY\"/lib-terminal.sh\n@@ -61,7 +63,7 @@ test_atom () {\n \n \tprintf '%s\\n' \"$3\" >expected\n \t$test_do $PREREQ \"basic atom: $ref $format\" '\n-\t\tgit for-each-ref --format=\"%($format)\" \"$ref\" >actual &&\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%($format)\" \"$ref\" >actual &&\n \t\tsanitize_pgp <actual >actual.clean &&\n \t\ttest_cmp expected actual.clean\n \t'\n@@ -88,7 +90,7 @@ test_atom () {\n \t\t\tesac &&\n \t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n \t\t\techo $expect >expected &&\n-\t\t\tgit for-each-ref --format=\"%(contents:size)\" \"$ref\" >actual &&\n+\t\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(contents:size)\" \"$ref\" >actual &&\n \t\t\ttest_cmp expected actual\n \t\t'\n \tfi\n@@ -281,7 +283,7 @@ test_atom tag HEAD ' '\n \n test_expect_success 'basic atom: refs/tags/testtag *raw' '\n \tgit cat-file commit refs/tags/testtag^{} >expected &&\n-\tgit for-each-ref --format=\"%(*raw)\" refs/tags/testtag >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(*raw)\" refs/tags/testtag >actual &&\n \tsanitize_pgp <expected >expected.clean &&\n \techo >>expected.clean &&\n \tsanitize_pgp <actual >actual.clean &&\n@@ -289,40 +291,45 @@ test_expect_success 'basic atom: refs/tags/testtag *raw' '\n '\n \n test_expect_success 'Check invalid atoms names are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'for-each-ref does not crash with -h' '\n-\ttest_expect_code 129 git for-each-ref -h >usage &&\n-\ttest_grep \"[Uu]sage: git for-each-ref \" usage &&\n-\ttest_expect_code 129 nongit git for-each-ref -h >usage &&\n-\ttest_grep \"[Uu]sage: git for-each-ref \" usage\n-'\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(INVALID)\" refs/heads\n+'\n+\n+if test \"$GIT_REFS_LIST_CMD\" = \"refs list\"\n+then\n+\tsay \"Skipping -h crash test for git refs list\"\n+else\n+\ttest_expect_success \"${GIT_REFS_LIST_CMD} does not crash with -h\" '\n+\t\ttest_expect_code 129 git ${GIT_REFS_LIST_CMD} -h >usage &&\n+\t\ttest_grep \"[Uu]sage: git for-each-ref \" usage &&\n+\t\ttest_expect_code 129 nongit git ${GIT_REFS_LIST_CMD} -h >usage &&\n+\t\ttest_grep \"[Uu]sage: git for-each-ref \" usage\n+\t'\n+fi\n \n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n-\tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n '\n \n test_expect_success 'Check valid format specifiers for date fields' '\n-\tgit for-each-ref --format=\"%(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:relative)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:short)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:local)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:iso8601)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:rfc2822)\" refs/heads\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:default)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:relative)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:short)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:local)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:iso8601)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:rfc2822)\" refs/heads\n '\n \n test_expect_success 'Check invalid format specifiers are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(authordate:INVALID)\" refs/heads\n '\n \n test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(objectname:short=0)\" &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(objectname:short=-1)\" &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(objectname:short=foo)\"\n '\n \n test_bad_atom () {\n@@ -337,7 +344,7 @@ test_bad_atom () {\n \n \tprintf '%s\\n' \"$3\" >expect\n \t$test_do $PREREQ \"err basic atom: $ref $format\" '\n-\t\ttest_must_fail git for-each-ref \\\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} \\\n \t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n \t\ttest_cmp expect error\n \t'\n@@ -395,10 +402,10 @@ test_date () {\n \t'refs/tags/testtag' '$tagger_date'\n \tEOF\n \t(\n-\t\tgit for-each-ref --shell \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --shell \\\n \t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n \t\t\trefs/heads &&\n-\t\tgit for-each-ref --shell \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --shell \\\n \t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n \t\t\trefs/tags\n \t) >actual &&\n@@ -429,16 +436,16 @@ test_expect_success 'Check format \"default-local\" date fields output' '\n # doesn't exit in error\n test_expect_success 'Check format \"relative\" date fields output' '\n \tf=relative &&\n-\t(git for-each-ref --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n-\tgit for-each-ref --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n+\t(git ${GIT_REFS_LIST_CMD} --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n '\n \n # We just check that this is the same as \"relative\" for now.\n test_expect_success 'Check format \"relative-local\" date fields output' '\n \ttest_date relative-local \\\n-\t\t\"$(git for-each-ref --format=\"%(committerdate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(authordate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(taggerdate:relative)\" refs/tags)\"\n+\t\t\"$(git ${GIT_REFS_LIST_CMD} --format=\"%(committerdate:relative)\" refs/heads)\" \\\n+\t\t\"$(git ${GIT_REFS_LIST_CMD} --format=\"%(authordate:relative)\" refs/heads)\" \\\n+\t\t\"$(git ${GIT_REFS_LIST_CMD} --format=\"%(taggerdate:relative)\" refs/tags)\"\n '\n \n test_expect_success 'Check format \"short\" date fields output' '\n@@ -488,7 +495,7 @@ test_expect_success 'Check format \"raw-local\" date fields output' '\n \n test_expect_success 'Check format of strftime date fields' '\n \techo \"my date is 2006-07-04\" >expected &&\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n \t  refs/heads >actual &&\n \ttest_cmp expected actual\n@@ -496,7 +503,7 @@ test_expect_success 'Check format of strftime date fields' '\n \n test_expect_success 'Check format of strftime-local date fields' '\n \techo \"my date is 2006-07-03\" >expected &&\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n \t  refs/heads >actual &&\n \ttest_cmp expected actual\n@@ -504,11 +511,11 @@ test_expect_success 'Check format of strftime-local date fields' '\n \n test_expect_success 'exercise strftime with odd fields' '\n \techo >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:)\" refs/heads >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:format:)\" refs/heads >actual &&\n \ttest_cmp expected actual &&\n \tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n \techo $long >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -519,7 +526,7 @@ refs/tags/testtag\n EOF\n \n test_expect_success 'Verify ascending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=refname >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --sort=refname >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -531,13 +538,13 @@ refs/heads/main\n EOF\n \n test_expect_success 'Verify descending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=-refname >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --sort=-refname >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'Give help even with invalid sort atoms' '\n-\ttest_expect_code 129 git for-each-ref --sort=bogus -h >actual 2>&1 &&\n-\tgrep \"^usage: git for-each-ref\" actual\n+\ttest_expect_code 129 git ${GIT_REFS_LIST_CMD} --sort=bogus -h >actual 2>&1 &&\n+\tgrep \"^usage: git ${GIT_REFS_LIST_CMD}\" actual\n '\n \n cat >expected <<\\EOF\n@@ -548,7 +555,7 @@ EOF\n test_expect_success 'exercise patterns with prefixes' '\n \tgit tag testtag-2 &&\n \ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n \ttest_cmp expected actual\n '\n@@ -561,7 +568,7 @@ EOF\n test_expect_success 'exercise glob patterns with prefixes' '\n \tgit tag testtag-2 &&\n \ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n \ttest_cmp expected actual\n '\n@@ -578,7 +585,7 @@ test_expect_success 'exercise patterns with prefix exclusions' '\n \t\tgit tag \"$tag\" || return 1\n \tdone &&\n \ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n \ttest_cmp expected actual\n '\n@@ -596,7 +603,7 @@ test_expect_success 'exercise patterns with pattern exclusions' '\n \t\tgit tag \"$tag\" || return 1\n \tdone &&\n \ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n \ttest_cmp expected actual\n '\n@@ -608,17 +615,17 @@ cat >expected <<\\EOF\n EOF\n \n test_expect_success 'Quoting style: shell' '\n-\tgit for-each-ref --shell --format=\"%(refname)\" >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --shell --format=\"%(refname)\" >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'Quoting style: perl' '\n-\tgit for-each-ref --perl --format=\"%(refname)\" >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --perl --format=\"%(refname)\" >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'Quoting style: python' '\n-\tgit for-each-ref --python --format=\"%(refname)\" >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --python --format=\"%(refname)\" >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -629,13 +636,13 @@ cat >expected <<\\EOF\n EOF\n \n test_expect_success 'Quoting style: tcl' '\n-\tgit for-each-ref --tcl --format=\"%(refname)\" >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --tcl --format=\"%(refname)\" >actual &&\n \ttest_cmp expected actual\n '\n \n for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n \ttest_expect_success \"more than one quoting style: $i\" \"\n-\t\ttest_must_fail git for-each-ref $i 2>err &&\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} $i 2>err &&\n \t\tgrep '^error: more than one quoting style' err\n \t\"\n done\n@@ -659,8 +666,8 @@ test_atom head push:track '[behind 1]'\n test_atom head push:trackshort '<'\n \n test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:track)\" 2>/dev/null &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:trackshort)\" 2>/dev/null\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname:track)\" 2>/dev/null &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname:trackshort)\" 2>/dev/null\n '\n \n test_expect_success 'Check that :track[short] works when upstream is invalid' '\n@@ -670,14 +677,14 @@ test_expect_success 'Check that :track[short] works when upstream is invalid' '\n \tEOF\n \ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n \tgit config branch.main.merge refs/heads/does-not-exist &&\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n \t\trefs/heads >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'Check for invalid refname format' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname:INVALID)\"\n '\n \n test_expect_success 'set up color tests' '\n@@ -694,24 +701,24 @@ test_expect_success 'set up color tests' '\n '\n \n test_expect_success TTY '%(color) shows color with a tty' '\n-\ttest_terminal git for-each-ref --format=\"$color_format\" >actual.raw &&\n+\ttest_terminal git ${GIT_REFS_LIST_CMD} --format=\"$color_format\" >actual.raw &&\n \ttest_decode_color <actual.raw >actual &&\n \ttest_cmp expected.color actual\n '\n \n test_expect_success '%(color) does not show color without tty' '\n-\tTERM=vt100 git for-each-ref --format=\"$color_format\" >actual &&\n+\tTERM=vt100 git ${GIT_REFS_LIST_CMD} --format=\"$color_format\" >actual &&\n \ttest_cmp expected.bare actual\n '\n \n test_expect_success '--color can override tty check' '\n-\tgit for-each-ref --color --format=\"$color_format\" >actual.raw &&\n+\tgit ${GIT_REFS_LIST_CMD} --color --format=\"$color_format\" >actual.raw &&\n \ttest_decode_color <actual.raw >actual &&\n \ttest_cmp expected.color actual\n '\n \n test_expect_success 'color.ui=always does not override tty check' '\n-\tgit -c color.ui=always for-each-ref --format=\"$color_format\" >actual &&\n+\tgit -c color.ui=always ${GIT_REFS_LIST_CMD} --format=\"$color_format\" >actual &&\n \ttest_cmp expected.bare actual\n '\n \n@@ -732,7 +739,7 @@ test_expect_success 'describe atom vs git describe' '\n \t(\n \t\tcd describe-repo &&\n \n-\t\tgit for-each-ref --format=\"%(objectname)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(objectname)\" \\\n \t\t\trefs/tags/ >obj &&\n \t\twhile read hash\n \t\tdo\n@@ -747,7 +754,7 @@ test_expect_success 'describe atom vs git describe' '\n \t\ttest_path_exists expect-contains-good &&\n \t\ttest_path_exists expect-contains-bad &&\n \n-\t\tgit for-each-ref --format=\"%(objectname) %(describe)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(objectname) %(describe)\" \\\n \t\t\trefs/tags/ >actual 2>err &&\n \t\ttest_cmp expect actual &&\n \t\ttest_must_be_empty err\n@@ -758,7 +765,7 @@ test_expect_success 'describe:tags vs describe --tags' '\n \t(\n \t\tcd describe-repo &&\n \t\tgit describe --tags >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:tags)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:tags)\" \\\n \t\t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual\n \t)\n@@ -772,7 +779,7 @@ test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n \t\t#\t  recent tag reachable from it\n \t\ttest_commit --no-tag file &&\n \t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:abbrev=14)\" \\\n \t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual &&\n \n@@ -784,7 +791,7 @@ test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n \t\t#\t  the name of the tag\n \t\tgit tag -a -m tagged tagname &&\n \t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:abbrev=14)\" \\\n \t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual &&\n \t\ttest tagname = $(cat actual)\n@@ -796,7 +803,7 @@ test_expect_success 'describe:match=... vs describe --match ...' '\n \t\tcd describe-repo &&\n \t\tgit tag -a -m \"tag foo\" tag-foo &&\n \t\tgit describe --match \"*-foo\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:match=\"*-foo\")\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:match=\"*-foo\")\" \\\n \t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual\n \t)\n@@ -807,7 +814,7 @@ test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n \t\tcd describe-repo &&\n \t\tgit tag -a -m \"tag bar\" tag-bar &&\n \t\tgit describe --exclude \"*-bar\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:exclude=\"*-bar\")\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:exclude=\"*-bar\")\" \\\n \t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual\n \t)\n@@ -824,7 +831,7 @@ test_expect_success 'deref with describe atom' '\n \n \t\ttagtwo\n \t\tEOF\n-\t\tgit for-each-ref --format=\"%(*describe)\" >actual &&\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(*describe)\" >actual &&\n \t\ttest_cmp expect actual\n \t)\n '\n@@ -837,7 +844,7 @@ test_expect_success 'err on bad describe atom arg' '\n \t\tcat >expect <<-\\EOF &&\n \t\tfatal: unrecognized %(describe) argument: baz\n \t\tEOF\n-\t\ttest_must_fail git for-each-ref --format=\"%(describe:baz)\" \\\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(describe:baz)\" \\\n \t\t\trefs/heads/master 2>actual &&\n \t\ttest_cmp expect actual &&\n \n@@ -846,7 +853,7 @@ test_expect_success 'err on bad describe atom arg' '\n \t\tcat >expect <<-\\EOF &&\n \t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n \t\tEOF\n-\t\ttest_must_fail git for-each-ref \\\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} \\\n \t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n \t\t\tref/heads/master 2>actual &&\n \t\ttest_cmp expect actual\n@@ -866,7 +873,7 @@ test_expect_success 'Check ambiguous head and tag refs (strict)' '\n \tgit commit -m \"Branch\" &&\n \tsetdate_and_increment &&\n \tgit tag -m \"Tagging at $datestamp\" main &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -877,7 +884,7 @@ EOF\n \n test_expect_success 'Check ambiguous head and tag refs (loose)' '\n \tgit config --bool core.warnambiguousrefs false &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -890,7 +897,7 @@ test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n \tgit checkout main &&\n \tgit tag ambiguous testtag^0 &&\n \tgit branch ambiguous testtag^0 &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -923,7 +930,7 @@ test_expect_success 'an unusual tag with an incomplete line' '\n \tbogo=$(git cat-file tag bogo) &&\n \tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n \tgit tag -f bogo \"$bogo\" &&\n-\tgit for-each-ref --format \"%(body)\" refs/tags/bogo\n+\tgit ${GIT_REFS_LIST_CMD} --format \"%(body)\" refs/tags/bogo\n \n '\n \n@@ -1000,7 +1007,7 @@ test_atom refs/tags/signed-empty contents \"$sig\"\n \n test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n \tgit cat-file tag refs/tags/signed-empty >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n \tsanitize_pgp <expected >expected.clean &&\n \techo >>expected.clean &&\n \tsanitize_pgp <actual >actual.clean &&\n@@ -1018,7 +1025,7 @@ $sig\"\n \n test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n \tgit cat-file tag refs/tags/signed-short >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-short >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/tags/signed-short >actual &&\n \tsanitize_pgp <expected >expected.clean &&\n \techo >>expected.clean &&\n \tsanitize_pgp <actual >actual.clean &&\n@@ -1040,7 +1047,7 @@ $sig\"\n \n test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n \tgit cat-file tag refs/tags/signed-long >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-long >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/tags/signed-long >actual &&\n \tsanitize_pgp <expected >expected.clean &&\n \techo >>expected.clean &&\n \tsanitize_pgp <actual >actual.clean &&\n@@ -1062,10 +1069,10 @@ test_atom refs/mytrees/first contents \"\"\n test_expect_success 'basic atom: refs/mytrees/first raw' '\n \tgit cat-file tree refs/mytrees/first >expected &&\n \techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/mytrees/first >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/mytrees/first >actual &&\n \ttest_cmp expected actual &&\n \tgit cat-file -s refs/mytrees/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1079,10 +1086,10 @@ test_atom refs/myblobs/first contents \"\"\n test_expect_success 'basic atom: refs/myblobs/first raw' '\n \tgit cat-file blob refs/myblobs/first >expected &&\n \techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/myblobs/first >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/myblobs/first >actual &&\n \ttest_cmp expected actual &&\n \tgit cat-file -s refs/myblobs/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1127,7 +1134,7 @@ test_expect_success 'Verify sorts with raw' '\n \trefs/myblobs/blob4\n \trefs/heads/main\n \tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --sort=raw \\\n \t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n \ttest_cmp expected actual\n '\n@@ -1146,7 +1153,7 @@ test_expect_success 'Verify sorts with raw:size' '\n \trefs/mytrees/first\n \trefs/heads/main\n \tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw:size \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --sort=raw:size \\\n \t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n \ttest_cmp expected actual\n '\n@@ -1166,7 +1173,7 @@ test_expect_success 'validate raw atom with %(if:equals)' '\n \tnot equals\n \tnot equals\n \tEOF\n-\tgit for-each-ref --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n \t\trefs/myblobs/ refs/heads/ >actual &&\n \ttest_cmp expected actual\n '\n@@ -1186,7 +1193,7 @@ test_expect_success 'validate raw atom with %(if:notequals)' '\n \trefs/myblobs/blob8\n \trefs/myblobs/first\n \tEOF\n-\tgit for-each-ref --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n \t\trefs/myblobs/ refs/heads/ >actual &&\n \ttest_cmp expected actual\n '\n@@ -1203,55 +1210,55 @@ test_expect_success 'empty raw refs with %(if)' '\n \trefs/myblobs/blob8 empty\n \trefs/myblobs/first not empty\n \tEOF\n-\tgit for-each-ref --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n \t\trefs/myblobs/ >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success '%(raw) with --python must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --python\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" --python\n '\n \n test_expect_success '%(raw) with --tcl must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --tcl\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" --tcl\n '\n \n test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n \tcmp blob1 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n \tcmp blob3 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n \tcmp blob8 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n \tcmp one actual &&\n \tgit cat-file tree refs/mytrees/first > expected &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n \tcmp expected actual\n '\n \n test_expect_success '%(raw) with --shell must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --shell\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" --shell\n '\n \n test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --sort=raw --shell\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" --sort=raw --shell\n '\n \n test_expect_success '%(raw:size) with --shell' '\n-\tgit for-each-ref --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n-\tgit for-each-ref --format=\"%(raw:size)\" --shell >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw:size)\" --shell >actual &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'for-each-ref --format compare with cat-file --batch' '\n+test_expect_success \"${GIT_REFS_LIST_CMD} --format compare with cat-file --batch\" '\n \tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n-\tgit for-each-ref --format=\"%(objectname) %(objecttype) %(objectsize)\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(objectname) %(objecttype) %(objectsize)\n %(raw)\" refs/mytrees/first >actual &&\n \ttest_cmp expected actual\n '\n@@ -1262,7 +1269,7 @@ test_expect_success 'verify sorts with contents:size' '\n \trefs/heads/newtag\n \trefs/heads/ambiguous\n \tEOF\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\t--sort=contents:size refs/heads/ >actual &&\n \ttest_cmp expect actual\n '\n@@ -1294,7 +1301,7 @@ test_expect_success 'Verify sort with multiple keys' '\n \t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n \t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n \tEOF\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n \t\t--sort=-refname \\\n \t\t--sort=taggeremail \\\n@@ -1314,7 +1321,7 @@ test_expect_success 'equivalent sorts fall back on refname' '\n \t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n \t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n \tEOF\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n \t\t--sort=taggerdate \\\n \t\t\"refs/tags/multi-*\" >actual &&\n@@ -1332,7 +1339,7 @@ test_expect_success '--no-sort cancels the previous sort keys' '\n \t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n \t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n \tEOF\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n \t\t--sort=-refname \\\n \t\t--sort=taggeremail \\\n@@ -1356,7 +1363,7 @@ test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n \n \t# Sort the results with `sort` for a consistent comparison against\n \t# expected\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(refname)\" \\\n \t\t--no-sort \\\n \t\t\"refs/tags/multi-*\" | sort >actual &&\n@@ -1387,7 +1394,7 @@ test_expect_success 'sort by date defaults to full timestamp' '\n \t1707341660 refs/tags/custom-dates-1\n \tEOF\n \n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(creatordate:unix) %(refname)\" \\\n \t\t--sort=creatordate \\\n \t\t\"refs/tags/custom-dates-*\" >actual &&\n@@ -1402,7 +1409,7 @@ test_expect_success 'sort by custom date format' '\n \t21:34:20 refs/tags/custom-dates-1\n \tEOF\n \n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n \t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n \t\t\"refs/tags/custom-dates-*\" >actual &&\n@@ -1411,10 +1418,10 @@ test_expect_success 'sort by custom date format' '\n \n test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n \ttest_when_finished \"git checkout main\" &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n \tsed -e \"s/^\\* /  /\" actual >expect &&\n \tgit checkout --orphan orphaned-branch &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -1451,9 +1458,9 @@ test_trailer_option () {\n \ttitle=$1 option=$2\n \tcat >expect\n \ttest_expect_success $prereq \"$title\" '\n-\t\tgit for-each-ref --format=\"%($option)\" refs/heads/main >actual &&\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%($option)\" refs/heads/main >actual &&\n \t\ttest_cmp expect actual &&\n-\t\tgit for-each-ref --format=\"%(contents:$option)\" refs/heads/main >actual &&\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(contents:$option)\" refs/heads/main >actual &&\n \t\ttest_cmp expect actual\n \t'\n }\n@@ -1583,7 +1590,7 @@ test_expect_success 'multiple %(trailers) use their own options' '\n \tEOF\n \tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n \tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n-\tgit for-each-ref --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n \tcat >expect <<-\\EOF &&\n \toneWfooXoneWbar\n \ttwoYbazZtwoYqux\n@@ -1596,9 +1603,9 @@ test_failing_trailer_option () {\n \tcat >expect\n \ttest_expect_success \"$title\" '\n \t\t# error message cannot be checked under i18n\n-\t\ttest_must_fail git for-each-ref --format=\"%($option)\" refs/heads/main 2>actual &&\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%($option)\" refs/heads/main 2>actual &&\n \t\ttest_cmp expect actual &&\n-\t\ttest_must_fail git for-each-ref --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n \t\ttest_cmp expect actual\n \t'\n }\n@@ -1617,14 +1624,14 @@ test_expect_success 'if arguments, %(contents:trailers) shows error if colon is\n \tcat >expect <<-EOF &&\n \tfatal: unrecognized %(contents) argument: trailersonly\n \tEOF\n-\ttest_must_fail git for-each-ref --format=\"%(contents:trailersonly)\" 2>actual &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(contents:trailersonly)\" 2>actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'basic atom: head contents:trailers' '\n-\tgit for-each-ref --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n \tsanitize_pgp <actual >actual.clean &&\n-\t# git for-each-ref ends with a blank line\n+\t# git ${GIT_REFS_LIST_CMD} ends with a blank line\n \tcat >expect <<-EOF &&\n \t$(cat trailers)\n \n@@ -1633,23 +1640,23 @@ test_expect_success 'basic atom: head contents:trailers' '\n '\n \n test_expect_success 'basic atom: rest must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(rest)\" refs/heads/main\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(rest)\" refs/heads/main\n '\n \n test_expect_success 'HEAD atom does not take arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(HEAD:foo)\" 2>err &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(HEAD:foo)\" 2>err &&\n \techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n \ttest_cmp expect err\n '\n \n test_expect_success 'subject atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(subject:foo)\" 2>err &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(subject:foo)\" 2>err &&\n \techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n \ttest_cmp expect err\n '\n \n test_expect_success 'refname atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:foo)\" 2>err &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname:foo)\" 2>err &&\n \techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n \ttest_cmp expect err\n '\n@@ -1673,7 +1680,7 @@ test_expect_success 'trailer parsing not fooled by --- line' '\n \t\techo \"trailer: right\" &&\n \t\techo\n \t} >expect &&\n-\tgit for-each-ref --format=\"%(trailers)\" refs/heads/main >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(trailers)\" refs/heads/main >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -1686,7 +1693,7 @@ refs/heads/main\n EOF\n \n test_expect_success 'Verify usage of %(symref) atom' '\n-\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref)\" refs/heads/sym >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1695,7 +1702,7 @@ heads/main\n EOF\n \n test_expect_success 'Verify usage of %(symref:short) atom' '\n-\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:short)\" refs/heads/sym >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1705,12 +1712,12 @@ heads/main\n EOF\n \n test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual &&\n \n-\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1720,8 +1727,8 @@ refs/heads\n EOF\n \n test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1745,38 +1752,38 @@ test_expect_success ':remotename and :remoteref' '\n \t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n \t\tdo\n \t\t\techo \"${pair#*=}\" >expect &&\n-\t\t\tgit for-each-ref --format=\"${pair%=*}\" \\\n+\t\t\tgit ${GIT_REFS_LIST_CMD} --format=\"${pair%=*}\" \\\n \t\t\t\trefs/heads/main >actual &&\n \t\t\ttest_cmp expect actual || exit 1\n \t\tdone &&\n \t\tgit branch push-simple &&\n \t\tgit config branch.push-simple.pushRemote from &&\n-\t\tactual=\"$(git for-each-ref \\\n+\t\tactual=\"$(git ${GIT_REFS_LIST_CMD} \\\n \t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n \t\t\trefs/heads/push-simple)\" &&\n \t\ttest from, = \"$actual\"\n \t)\n '\n \n-test_expect_success 'for-each-ref --ignore-case ignores case' '\n-\tgit for-each-ref --format=\"%(refname)\" refs/heads/MAIN >actual &&\n+test_expect_success \"${GIT_REFS_LIST_CMD} --ignore-case ignores case\" '\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" refs/heads/MAIN >actual &&\n \ttest_must_be_empty actual &&\n \n \techo refs/heads/main >expect &&\n-\tgit for-each-ref --format=\"%(refname)\" --ignore-case \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --ignore-case \\\n \t\trefs/heads/MAIN >actual &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'for-each-ref --omit-empty works' '\n-\tgit for-each-ref --format=\"%(refname)\" >actual &&\n+test_expect_success \"${GIT_REFS_LIST_CMD} --omit-empty works\" '\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" >actual &&\n \ttest_line_count -gt 1 actual &&\n-\tgit for-each-ref --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n \techo refs/heads/main >expect &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n+test_expect_success \"${GIT_REFS_LIST_CMD} --ignore-case works on multiple sort keys\" '\n \t# name refs numerically to avoid case-insensitive filesystem conflicts\n \tnr=0 &&\n \tfor email in a A b B\n@@ -1789,7 +1796,7 @@ test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n \t\t\treturn 1\n \t\tdone\n \tdone &&\n-\tgit for-each-ref --ignore-case \\\n+\tgit ${GIT_REFS_LIST_CMD} --ignore-case \\\n \t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n \t\t--sort=refname \\\n \t\t--sort=subject \\\n@@ -1816,13 +1823,13 @@ test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'for-each-ref reports broken tags' '\n+test_expect_success \"${GIT_REFS_LIST_CMD} reports broken tags\" '\n \tgit tag -m \"good tag\" broken-tag-good HEAD &&\n \tgit cat-file tag broken-tag-good >good &&\n \tsed s/commit/blob/ <good >bad &&\n \tbad=$(git hash-object -w -t tag bad) &&\n \tgit update-ref refs/tags/broken-tag-bad $bad &&\n-\ttest_must_fail git for-each-ref --format=\"%(*objectname)\" \\\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(*objectname)\" \\\n \t\trefs/tags/broken-tag-*\n '\n \n@@ -1884,21 +1891,21 @@ test_expect_success 'set up tag with signature and trailers' '\n # use \"separator=\" here to suppress the terminating newline\n test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n \n-test_expect_success 'git for-each-ref --stdin: empty' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} --stdin: empty\" '\n \t>in &&\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\tgit for-each-ref --format=\"%(refname)\" >expect &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --stdin <in >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" >expect &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'git for-each-ref --stdin: fails if extra args' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} --stdin: fails if extra args\" '\n \t>in &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname)\" \\\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\t--stdin refs/heads/extra <in 2>err &&\n \tgrep \"unknown arguments supplied with --stdin\" err\n '\n \n-test_expect_success 'git for-each-ref --stdin: matches' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} --stdin: matches\" '\n \tcat >in <<-EOF &&\n \trefs/tags/multi*\n \trefs/heads/amb*\n@@ -1917,24 +1924,24 @@ test_expect_success 'git for-each-ref --stdin: matches' '\n \trefs/tags/multiline\n \tEOF\n \n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --stdin <in >actual &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'git for-each-ref with non-existing refs' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} with non-existing refs\" '\n \tcat >in <<-EOF &&\n \trefs/heads/this-ref-does-not-exist\n \trefs/tags/bogus\n \tEOF\n \n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --stdin <in >actual &&\n \ttest_must_be_empty actual &&\n \n-\txargs git for-each-ref --format=\"%(refname)\" <in >actual &&\n+\txargs git ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" <in >actual &&\n \ttest_must_be_empty actual\n '\n \n-test_expect_success 'git for-each-ref with nested tags' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} with nested tags\" '\n \tgit tag -am \"Normal tag\" nested/base HEAD &&\n \tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n \tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n@@ -1950,14 +1957,14 @@ test_expect_success 'git for-each-ref with nested tags' '\n \trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n \tEOF\n \n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n \t\trefs/tags/nested/ >actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'is-base atom with non-commits' '\n-\tgit for-each-ref --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n \tgrep \"(HEAD) refs/heads/main\" out &&\n \n \ttest_line_count = 2 err &&\n@@ -2034,7 +2041,7 @@ test_expect_success GPGSSH 'setup for signature atom using ssh' '\n test_expect_success GPG2 'bare signature atom' '\n \tgit verify-commit first-signed 2>expect &&\n \techo  >>expect &&\n-\tgit for-each-ref refs/tags/first-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/first-signed \\\n \t\t--format=\"%(signature)\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2048,7 +2055,7 @@ test_expect_success GPG 'show good signature with custom format' '\n \t73D758744BE721698EC54E8713B6F51ECDDE430D\n \t73D758744BE721698EC54E8713B6F51ECDDE430D\n \tEOF\n-\tgit for-each-ref refs/tags/first-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/first-signed \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2063,7 +2070,7 @@ test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n \n \tEOF\n \tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n-\tgit for-each-ref refs/tags/eighth-signed-ssh \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/eighth-signed-ssh \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2082,7 +2089,7 @@ test_expect_success GPG 'signature atom with grade option and bad signature' '\n \n \n \tEOF\n-\tgit for-each-ref refs/tags/third-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/third-signed \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2095,7 +2102,7 @@ test_expect_success GPG 'show untrusted signature with custom format' '\n \tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n \tD4BE22311AD3131E5EDA29A461092E85B7227189\n \tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/fourth-signed \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2108,7 +2115,7 @@ test_expect_success GPG 'show untrusted signature with undefined trust level' '\n \tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n \tD4BE22311AD3131E5EDA29A461092E85B7227189\n \tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/fourth-signed \\\n \t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2121,7 +2128,7 @@ test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n \t73D758744BE721698EC54E8713B6F51ECDDE430D\n \t73D758744BE721698EC54E8713B6F51ECDDE430D\n \tEOF\n-\tgit for-each-ref refs/tags/sixth-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/sixth-signed \\\n \t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2134,7 +2141,7 @@ test_expect_success GPG 'show unknown signature with custom format' '\n \n \n \tEOF\n-\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git ${GIT_REFS_LIST_CMD} \\\n \t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2147,7 +2154,7 @@ test_expect_success GPG 'show lack of signature with custom format' '\n \n \n \tEOF\n-\tgit for-each-ref refs/tags/seventh-unsigned \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/seventh-unsigned \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n-- \n2.34.1\n\n"},{"id":"520813","messageId":"0d3fe70e-2064-4206-9673-c0589a4445f1@free.fr","threadId":"63703","inReplyTo":"20250627074934.1761897-2-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH 1/2] builtin/refs: add list subcommand","fromName":"Jean-Noël Avila","fromEmail":"jn.avila@free.fr","sentAt":"2025-06-27T16:27:04Z","receivedAt":"2025-06-27T16:33:55Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"Hello,\n\nI'm only focusing on the documentation part.\n\nLe 27/06/2025 à 09:49, Meet Soni a écrit :\n>\n> ---\n>  Documentation/git-refs.adoc |  95 +++++++++++++++++++++++++++++++\n>  builtin/refs.c              | 110 ++++++++++++++++++++++++++++++++++++\n>  2 files changed, 205 insertions(+)\n> \n> diff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\n> index 4d6dc994f9..d8f81eaabd 100644\n> --- a/Documentation/git-refs.adoc\n> +++ b/Documentation/git-refs.adoc\n> @@ -11,6 +11,13 @@ SYNOPSIS\n>  [synopsis]\n>  git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n>  git refs verify [--strict] [--verbose]\n> +git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n\nplease use spaces around '|' in alternative options:\n[--shell | --perl | --python | --tcl]\n\n> +\t      [(--sort=<key>)...] [--format=<format>]\n\nThis \"[(--sort=<key>)...]\" form is new to me. It abides by the synopsis\nsyntax. As I understand it, the user can specify a number of\n--sort=<key> on the command line.\n\nFrom the description below, it seems that for --format, the actual\nsyntax should be:\n\n[--format[=<format>]]\n\n\n> +\t      [--include-root-refs] [ --stdin | <pattern>... ]\n\nHere no spaces, after '[' or before ']':\n[--stdin | <pattern>...]\n\nFor syntax precedence, we have not documented anything, but the obvious\nmeaning is \"--stdin or a number of <pattern>. OK\n\n> +\t      [--points-at=<object>]\n> +\t      [--merged[=<object>]] [--no-merged[=<object>]]\n> +\t      [--contains[=<object>]] [--no-contains[=<object>]]\n> +\t      [--exclude=<pattern> ...]\n\nWhat are the ... meaning here ? Is it --exclude= followed by a number of\n<pattern>? What is the separator then?\nOr is it like for --sort?\n\nMaybe use another placeholder name to differentiate from the <pattern>\nalternative to --stdin\n\n>  \n>  DESCRIPTION\n>  -----------\n> @@ -26,6 +33,11 @@ migrate::\n>  verify::\n>  \tVerify reference database consistency.\n>  \n> +list::\n\nOK, synopsis is used in the first part, but this man page has not been\nfully converted. Let's stick to the previous style.\n\n> +\tList references in the repository with support for filtering, formatting,\n> +\tand sorting. This subcommand uses the same core logic as\n> +\tlinkgit:git-for-each-ref[1] and offers equivalent functionality.\n> +\n>  OPTIONS\n>  -------\n>  \n> @@ -57,6 +69,89 @@ The following options are specific to 'git refs verify':\n>  --verbose::\n>  \tWhen verifying the reference database consistency, be chatty.\n>  \n> +The following options are specific to 'git refs list':\n> +\n> +<pattern>...::\n> +\tIf one or more patterns are given, only refs are shown that\n> +\tmatch against at least one pattern, either using fnmatch(3) or\n> +\tliterally, in the latter case matching completely or from the\n> +\tbeginning up to a slash.\n> +> +--stdin::\n> +\tIf `--stdin` is supplied, then the list of patterns is read from\n> +\tstandard input instead of from the argument list.> +\n> +--count=<count>::\n> +\tBy default the command shows all refs that match\n> +\t`<pattern>`.  This option makes it stop after showing\n> +\tthat many refs.\n\nPlease revert the sentences and remove the \"this option makes it\". The\noption description should state first the action of the option, then\ntalk about default values, behavior, ...\n\n> +\n> +--sort=<key>::\n> +\tA field name to sort on.  Prefix `-` to sort in\n\nUse a verb in imperative mood as much as possible.\n\n> +\tdescending order of the value.  When unspecified,\n> +\t`refname` is used.  You may use the --sort=<key> option\n> +\tmultiple times, in which case the last key becomes the primary\n> +\tkey.\n> +\n> +--format=<format>::\n\nCite the form with optional param\n\n> +\tA string that interpolates `%(fieldname)` from a ref being shown and\n> +\tthe object it points at. In addition, the string literal `%%`\n> +\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n> +\tthe character with hex code `xx`. For example, `%00` interpolates to\n\nthe ASCII character only I guess: encoding matters here as we are\nusually in UTF-8.\n\n> +\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n> ++\n> +When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n> +TAB %(refname)`.\n> +\n> +--color[=<when>]::\n> +\tRespect any colors specified in the `--format` option. The\n> +\t`<when>` field must be one of `always`, `never`, or `auto` (if\n> +\t`<when>` is absent, behave as if `always` was given).\n> +\n> +--shell::\n> +--perl::\n> +--python::\n> +--tcl::\n> +\tIf given, strings that substitute `%(fieldname)`\n\nPersonal taste: \"If given\" is useless here.\n\n> +\tplaceholders are quoted as string literals suitable for\n> +\tthe specified host language.  This is meant to produce\n> +\ta scriptlet that can directly be `eval`ed.\n> +\n> +--points-at=<object>::\n> +\tOnly list refs which points at the given object.\n> +\n> +--merged[=<object>]::\n> +\tOnly list refs whose tips are reachable from the\n> +\tspecified commit (HEAD if not specified).\n> +\n> +--no-merged[=<object>]::\n> +\tOnly list refs whose tips are not reachable from the\n> +\tspecified commit (HEAD if not specified).\n> +\n> +--contains[=<object>]::\n> +\tOnly list refs which contain the specified commit (HEAD if not\n> +\tspecified).\n> +\n> +--no-contains[=<object>]::\n> +\tOnly list refs which don't contain the specified commit (HEAD\n> +\tif not specified).\n> +\n> +--ignore-case::\n> +\tSorting and filtering refs are case insensitive.\n> +\n> +--omit-empty::\n> +\tDo not print a newline after formatted refs where the format expands\n> +\tto the empty string.\n> +\n> +--exclude=<pattern>::\n> +\tIf one or more patterns are given, only refs which do not match\n> +\tany excluded pattern(s) are shown. Matching is done using the\n> +\tsame rules as `<pattern>` above.\n> +\n> +--include-root-refs::\n> +\tList root refs (HEAD and pseudorefs) apart from regular refs.\n\nThe description seems a bit off. Did you really mean \"apart from\", not\n\"along with\"?\n\n\nThanks\n\nJean-Noël\n"},{"id":"520814","messageId":"xmqqh60183ku.fsf@gitster.g","threadId":"63703","inReplyTo":"20250627074934.1761897-1-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-27T18:03:13Z","receivedAt":"2025-06-27T18:03:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Meet Soni <meetsoni3017@gmail.com> writes:\n\n>   - Remove legacy or obscure options that are no longer needed.\n\nSuch as?\n\n>   - Add improvements that wouldn't make sense to bolt onto legacy\n>     commands.\n\nSuch as?\n\nWhile I agree that there may be cases that the above goals in\ngeneral would bring us improvements, I think neither of these two\napplies to for-each-ref.  People are using for-each-ref to iterate\nover and enumerate refs already, and if you are to add some new\nfeatures to \"git refs list\", they certainly will demand these new\ngoodies to be added to for-each-ref as well.\n\nSo, I dunno.\n"},{"id":"520816","messageId":"xmqq7c0x833f.fsf@gitster.g","threadId":"63703","inReplyTo":"0d3fe70e-2064-4206-9673-c0589a4445f1@free.fr","subject":"Re: [GSoC][RFC PATCH 1/2] builtin/refs: add list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-27T18:13:40Z","receivedAt":"2025-06-27T18:13:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jean-Noël Avila <jn.avila@free.fr> writes:\n\n> Hello,\n>\n> I'm only focusing on the documentation part.\n>\n> Le 27/06/2025 à 09:49, Meet Soni a écrit :\n>>\n>> ---\n>>  Documentation/git-refs.adoc |  95 +++++++++++++++++++++++++++++++\n>>  builtin/refs.c              | 110 ++++++++++++++++++++++++++++++++++++\n>>  2 files changed, 205 insertions(+)\n>> \n>> diff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\n>> index 4d6dc994f9..d8f81eaabd 100644\n>> --- a/Documentation/git-refs.adoc\n>> +++ b/Documentation/git-refs.adoc\n>> @@ -11,6 +11,13 @@ SYNOPSIS\n>>  [synopsis]\n>>  git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n>>  git refs verify [--strict] [--verbose]\n>> +git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n>\n> please use spaces around '|' in alternative options:\n> [--shell | --perl | --python | --tcl]\n\nAs this seems to be copied and pasted with minimum modification,\nperhaps we are better off if you sent a \"here is how you should do\nit\" patch against Documentation/git-for-each-ref.adoc file where\nthis was copied from.\n"},{"id":"520841","messageId":"aF-iSSOmcplFjKQt@ArchLinux","threadId":"63703","inReplyTo":"xmqqh60183ku.fsf@gitster.g","subject":"Re: [GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"shejialuo","fromEmail":"shejialuo@gmail.com","sentAt":"2025-06-28T08:05:29Z","receivedAt":"2025-06-28T08:05:19Z","isPatch":true,"sender":{"key":"shejialuo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56911263?v=4"},"body":"On Fri, Jun 27, 2025 at 11:03:13AM -0700, Junio C Hamano wrote:\n> Meet Soni <meetsoni3017@gmail.com> writes:\n> \n> >   - Remove legacy or obscure options that are no longer needed.\n> \n> Such as?\n> \n> >   - Add improvements that wouldn't make sense to bolt onto legacy\n> >     commands.\n> \n> Such as?\n> \n> While I agree that there may be cases that the above goals in\n> general would bring us improvements, I think neither of these two\n> applies to for-each-ref.  People are using for-each-ref to iterate\n> over and enumerate refs already, and if you are to add some new\n> features to \"git refs list\", they certainly will demand these new\n> goodies to be added to for-each-ref as well.\n> \n\nIf so, we would make \"git refs list\" to place \"git for-each-ref\" at\nall. However, in the current implementation, we indeed introduce\nduplicate code path if we decide to do above.\n\nThanks,\nJialuo\n"},{"id":"520871","messageId":"20250629110652.123890-1-jn.avila@free.fr","threadId":"63703","inReplyTo":"20250627074934.1761897-2-meetsoni3017@gmail.com","subject":"[PATCH] doc:git-for-each-ref: fix styling and typos","fromName":"Jean-Noël Avila","fromEmail":"jn.avila@free.fr","sentAt":"2025-06-29T11:05:31Z","receivedAt":"2025-06-29T11:07:15Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"This commit fixes the synopsis syntax writing and changes the wording of a few\ndescriptions to be more consistent with the rest of the documentation.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n Documentation/git-for-each-ref.adoc | 30 ++++++++++++++---------------\n 1 file changed, 14 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\nindex 5ef89fc0fe..c2b2660771 100644\n--- a/Documentation/git-for-each-ref.adoc\n+++ b/Documentation/git-for-each-ref.adoc\n@@ -8,13 +8,13 @@ git-for-each-ref - Output information on each ref\n SYNOPSIS\n --------\n [verse]\n-'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]\n-\t\t   [(--sort=<key>)...] [--format=<format>]\n-\t\t   [--include-root-refs] [ --stdin | <pattern>... ]\n+'git for-each-ref' [--count=<count>] [--shell | --perl | --python | --tcl]\n+\t\t   [(--sort=<key>)...] [--format[=<format>]]\n+\t\t   [--include-root-refs] [--stdin | <pattern>...]\n \t\t   [--points-at=<object>]\n \t\t   [--merged[=<object>]] [--no-merged[=<object>]]\n \t\t   [--contains[=<object>]] [--no-contains[=<object>]]\n-\t\t   [--exclude=<pattern> ...]\n+\t\t   [(--exclude=<excluded-pattern>)...]\n \n DESCRIPTION\n -----------\n@@ -35,13 +35,11 @@ OPTIONS\n \tbeginning up to a slash.\n \n --stdin::\n-\tIf `--stdin` is supplied, then the list of patterns is read from\n-\tstandard input instead of from the argument list.\n+\tThe list of patterns is read from standard input instead of from\n+\tthe argument list.\n \n --count=<count>::\n-\tBy default the command shows all refs that match\n-\t`<pattern>`.  This option makes it stop after showing\n-\tthat many refs.\n+\tStop after showing <count> refs.\n \n --sort=<key>::\n \tA field name to sort on.  Prefix `-` to sort in\n@@ -50,7 +48,7 @@ OPTIONS\n \tmultiple times, in which case the last key becomes the primary\n \tkey.\n \n---format=<format>::\n+ --format[=<format>]::\n \tA string that interpolates `%(fieldname)` from a ref being shown and\n \tthe object it points at. In addition, the string literal `%%`\n \trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n@@ -100,10 +98,10 @@ TAB %(refname)`.\n \tDo not print a newline after formatted refs where the format expands\n \tto the empty string.\n \n---exclude=<pattern>::\n-\tIf one or more patterns are given, only refs which do not match\n-\tany excluded pattern(s) are shown. Matching is done using the\n-\tsame rules as `<pattern>` above.\n+--exclude=<excluded-pattern>::\n+\tIf one or more --exclude options are given, only refs which do not\n+\tmatch any _<excluded-pattern>_ parameters are shown. Matching is done\n+\tusing the same rules as _<pattern>_ above.\n \n --include-root-refs::\n \tList root refs (HEAD and pseudorefs) apart from regular refs.\n@@ -131,8 +129,8 @@ refname::\n \t`refs/tags/foo` into `tags/foo` and `%(refname:rstrip=-1)`\n \tturns `refs/tags/foo` into `refs`). When the ref does not have\n \tenough components, the result becomes an empty string if\n-\tstripping with positive <N>, or it becomes the full refname if\n-\tstripping with negative <N>.  Neither is an error.\n+\tstripping with positive _<N>_, or it becomes the full refname if\n+\tstripping with negative _<N>_.  Neither is an error.\n +\n `strip` can be used as a synonym to `lstrip`.\n \n-- \n2.49.0\n\n"},{"id":"520897","messageId":"CAPhwyn01spdT-hFxVPcr4cnwBJ=YL3Ty-johXvGKXKRdCTpHXg@mail.gmail.com","threadId":"63703","inReplyTo":"xmqqh60183ku.fsf@gitster.g","subject":"Re: [GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-06-30T03:53:44Z","receivedAt":"2025-06-30T03:53:55Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"On Fri, 27 Jun 2025 at 23:33, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Meet Soni <meetsoni3017@gmail.com> writes:\n>\n> >   - Remove legacy or obscure options that are no longer needed.\n>\n> Such as?\n>\n> >   - Add improvements that wouldn't make sense to bolt onto legacy\n> >     commands.\n>\n> Such as?\n>\n> While I agree that there may be cases that the above goals in\n> general would bring us improvements, I think neither of these two\n> applies to for-each-ref.  People are using for-each-ref to iterate\n> over and enumerate refs already, and if you are to add some new\n> features to \"git refs list\", they certainly will demand these new\n> goodies to be added to for-each-ref as well.\n>\n> So, I dunno.\n\nTo clarify, I don't have specific options or improvements in mind right now.\nThe idea behind mentioning them was to acknowledge that having a consolidated\ninterface like git refs might open the door to such discussions.\n\nThe primary motivation here is to make ref-related commands discoverable at a\nsingle entry point - git refs, rather than scattered across several top-level\ncommands. The aim is to improve disoverability and set the stage for\npotential future\ncleanup or enhancements, should the community find value in doing so.\n\nPer mentor suggestion, this RFC was meant to invite broader input on whether\nthe community sees value in such a consolidation, and if so, what shape future\nrefinements (if any) might take.\n"},{"id":"520899","messageId":"CAPhwyn20tVSZfe2RgmYwoWJkKuMBj9S=Ji_zkYO0a0JMwnOSAQ@mail.gmail.com","threadId":"63703","inReplyTo":"0d3fe70e-2064-4206-9673-c0589a4445f1@free.fr","subject":"Re: [GSoC][RFC PATCH 1/2] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-06-30T04:28:53Z","receivedAt":"2025-06-30T04:29:06Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Thanks for the review, and apologies. I should've clarified earlier that the\ncurrent documentation was meant as a placeholder until the feature direction is\nfinalized. The intention was to help reviewers get a sense of the intended\nusage, and I plan to revise it more thoroughly once there's consensus on the\ninterface.\n\nAlso, noted the follow-up patch to improve the git-for-each-ref documentation.\nThanks for taking that initiative!\n\nThanks,\nMeet\n"},{"id":"520918","messageId":"xmqqo6u55npk.fsf@gitster.g","threadId":"63703","inReplyTo":"aF-iSSOmcplFjKQt@ArchLinux","subject":"Re: [GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-30T14:05:43Z","receivedAt":"2025-06-30T14:05:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"shejialuo <shejialuo@gmail.com> writes:\n\n> If so, we would make \"git refs list\" to place \"git for-each-ref\" at\n> all. However, in the current implementation, we indeed introduce\n> duplicate code path if we decide to do above.\n\nI do not know what you meant by your first sentence.\n\nIf you make \"git refs list <anything>\" a thin wrapper for \"git\nfor-each-ref <anthing>\", you can satisfy \"I want to teach any and\nall features related to references to the 'git refs' command\" while\nnot penalizing existing users.  After all, that is essentially what\n\"git branch\" and \"git tag\" do as their listing mode and supports the\nfeaturs from for-each-ref, isn't it?\n\n\n\n\n\n"},{"id":"520929","messageId":"xmqqjz4t44ei.fsf@gitster.g","threadId":"63703","inReplyTo":"20250629110652.123890-1-jn.avila@free.fr","subject":"Re: [PATCH] doc:git-for-each-ref: fix styling and typos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-30T15:48:05Z","receivedAt":"2025-06-30T15:48:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jean-Noël Avila <jn.avila@free.fr> writes:\n\n> This commit fixes the synopsis syntax writing and changes the wording of a few\n> descriptions to be more consistent with the rest of the documentation.\n>\n> Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>\n> ---\n>  Documentation/git-for-each-ref.adoc | 30 ++++++++++++++---------------\n>  1 file changed, 14 insertions(+), 16 deletions(-)\n\n\nIt is not making anything worse and all the changes I see here\n(except for a stray SP slipped in) are for the better, but it is\ncurious that this stops halfway.  Things I noticed:\n\n> diff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\n> index 5ef89fc0fe..c2b2660771 100644\n> --- a/Documentation/git-for-each-ref.adoc\n> +++ b/Documentation/git-for-each-ref.adoc\n> @@ -8,13 +8,13 @@ git-for-each-ref - Output information on each ref\n>  SYNOPSIS\n>  --------\n>  [verse]\n\nEventually we would switch to [synopsis] I presume?\n\n> -'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]\n> -\t\t   [(--sort=<key>)...] [--format=<format>]\n> -\t\t   [--include-root-refs] [ --stdin | <pattern>... ]\n> +'git for-each-ref' [--count=<count>] [--shell | --perl | --python | --tcl]\n> +\t\t   [(--sort=<key>)...] [--format[=<format>]]\n> +\t\t   [--include-root-refs] [--stdin | <pattern>...]\n>  \t\t   [--points-at=<object>]\n>  \t\t   [--merged[=<object>]] [--no-merged[=<object>]]\n>  \t\t   [--contains[=<object>]] [--no-contains[=<object>]]\n> -\t\t   [--exclude=<pattern> ...]\n> +\t\t   [(--exclude=<excluded-pattern>)...]\n>  \n>  DESCRIPTION\n>  -----------\n> @@ -35,13 +35,11 @@ OPTIONS\n>  \tbeginning up to a slash.\n>  \n>  --stdin::\n> -\tIf `--stdin` is supplied, then the list of patterns is read from\n> -\tstandard input instead of from the argument list.\n> +\tThe list of patterns is read from standard input instead of from\n> +\tthe argument list.\n>  \n>  --count=<count>::\n> -\tBy default the command shows all refs that match\n> -\t`<pattern>`.  This option makes it stop after showing\n> -\tthat many refs.\n> +\tStop after showing <count> refs.\n\nThis patch would have changed this to _<count>_, judging from what\nit did elsewhere.\n\n> @@ -50,7 +48,7 @@ OPTIONS\n>  \tmultiple times, in which case the last key becomes the primary\n>  \tkey.\n>  \n> ---format=<format>::\n> + --format[=<format>]::\n\nStray SP in the front?\n\n> @@ -100,10 +98,10 @@ TAB %(refname)`.\n>  \tDo not print a newline after formatted refs where the format expands\n>  \tto the empty string.\n>  \n> ---exclude=<pattern>::\n> -\tIf one or more patterns are given, only refs which do not match\n> -\tany excluded pattern(s) are shown. Matching is done using the\n> -\tsame rules as `<pattern>` above.\n> +--exclude=<excluded-pattern>::\n> +\tIf one or more --exclude options are given, only refs which do not\n> +\tmatch any _<excluded-pattern>_ parameters are shown. Matching is done\n> +\tusing the same rules as _<pattern>_ above.\n\nOK.  Doing the literal `--exclude` for options in the description is\nleft for future patches would not make it any worse, and adopting\n_<placeholder>_ convention makes it better.\n\n>  --include-root-refs::\n>  \tList root refs (HEAD and pseudorefs) apart from regular refs.\n> @@ -131,8 +129,8 @@ refname::\n>  \t`refs/tags/foo` into `tags/foo` and `%(refname:rstrip=-1)`\n>  \tturns `refs/tags/foo` into `refs`). When the ref does not have\n>  \tenough components, the result becomes an empty string if\n> -\tstripping with positive <N>, or it becomes the full refname if\n> -\tstripping with negative <N>.  Neither is an error.\n> +\tstripping with positive _<N>_, or it becomes the full refname if\n> +\tstripping with negative _<N>_.  Neither is an error.\n>  +\n>  `strip` can be used as a synonym to `lstrip`.\n"},{"id":"520957","messageId":"5900272.DvuYhMxLoT@cayenne","threadId":"63703","inReplyTo":"xmqqjz4t44ei.fsf@gitster.g","subject":"Re: [PATCH] doc:git-for-each-ref: fix styling and typos","fromName":"Jean-Noël AVILA","fromEmail":"jn.avila@free.fr","sentAt":"2025-06-30T18:55:08Z","receivedAt":"2025-06-30T18:55:28Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"On Monday, 30 June 2025 17:48:05 CEST Junio C Hamano wrote:\n> Jean-Noël Avila <jn.avila@free.fr> writes:\n> > This commit fixes the synopsis syntax writing and changes the wording of a\n> > few descriptions to be more consistent with the rest of the documentation.\n> > \n> > Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>\n> > ---\n> > \n> >  Documentation/git-for-each-ref.adoc | 30 ++++++++++++++---------------\n> >  1 file changed, 14 insertions(+), 16 deletions(-)\n> \n> It is not making anything worse and all the changes I see here\n> (except for a stray SP slipped in) are for the better, but it is\n> \n> curious that this stops halfway.  Things I noticed:\n\n\nI was just focusing on general style and synopsis syntax as an example for the \noriginal patch in this thread, not really trying to convert this page. I agree \nthat I should do the full monty while at it.\n\nWill roll a V2 then.\n\nThanks\n\nJean-Noël\n\n\n\n"},{"id":"520969","messageId":"xmqqldp9m1mr.fsf@gitster.g","threadId":"63703","inReplyTo":"CAPhwyn01spdT-hFxVPcr4cnwBJ=YL3Ty-johXvGKXKRdCTpHXg@mail.gmail.com","subject":"Re: [GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-30T20:10:36Z","receivedAt":"2025-06-30T20:10:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Meet Soni <meetsoni3017@gmail.com> writes:\n\n> To clarify, I don't have specific options or improvements in mind right now.\n> The idea behind mentioning them was to acknowledge that having a consolidated\n> interface like git refs might open the door to such discussions.\n\nOne complaint we heard a lot about Git in the past (I do not know if\npeople got used to and learned to live with, or they are still\ncomplaining about the same these days) was that there were always\nmultiple ways to do related things slightly differently.\n\nThe machinery of for-each-ref is shared by branch and tag to give\nthem feature-parity.  Adding yet another command that behaves\nslightly differently, with the intention to make it diverge even\nmore in the future, feels going backwards.\n\n> The primary motivation here is to make ref-related commands discoverable at a\n> single entry point - git refs, rather than scattered across several top-level\n> commands. The aim is to improve disoverability and set the stage for\n> potential future\n> cleanup or enhancements, should the community find value in doing so.\n\nWe need to be a bit careful.  There was a case that went in a\ntotally opposite direction.  Even though a single command was there\nin the beginning that can be used to pick a specific thing out of\nthe histories stored in a repository to externalize it in the file\nsystem to be worked on, it was later split into two commands as\nfolks thought it would make it more discoverable.\n\nEven though branches, tags, and symbolic refs like HEAD are all\nrefs, from the point of view of end-users, they are different things\nbuilt on top of the same underlying mechanism.  The fact that they\nshare the underlying mechanism does not necessarily mean that a\nsingle same command is easier to discover and work on them to the\nusers.\n\nIt may not hurt too much to have \"enumerate all refs, and for each\nref, allowing to limit them or to sort them with some criteria, and\nshow them in a customizable format\" as \"git refs list\", but we do\nneed to keep the feature parity with \"git for-each-ref\".  Adding\nunnecessary subtle differences would only confuse users.\n"},{"id":"521363","messageId":"aGpy8zQRsEjqX3mw@ArchLinux","threadId":"63703","inReplyTo":"xmqqo6u55npk.fsf@gitster.g","subject":"Re: [GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"shejialuo","fromEmail":"shejialuo@gmail.com","sentAt":"2025-07-06T12:58:27Z","receivedAt":"2025-07-06T12:58:31Z","isPatch":true,"sender":{"key":"shejialuo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56911263?v=4"},"body":"On Mon, Jun 30, 2025 at 07:05:43AM -0700, Junio C Hamano wrote:\n> shejialuo <shejialuo@gmail.com> writes:\n> \n> > If so, we would make \"git refs list\" to place \"git for-each-ref\" at\n> > all. However, in the current implementation, we indeed introduce\n> > duplicate code path if we decide to do above.\n> \n> I do not know what you meant by your first sentence.\n> \n\nSorry, I think I made you confused here. My meaning is exactly below\nwhat you have said.\n\n> If you make \"git refs list <anything>\" a thin wrapper for \"git\n> for-each-ref <anthing>\", you can satisfy \"I want to teach any and\n> all features related to references to the 'git refs' command\" while\n> not penalizing existing users.  After all, that is essentially what\n> \"git branch\" and \"git tag\" do as their listing mode and supports the\n> featurs from for-each-ref, isn't it?\n\nThat's right. I just want to let Meet know, if we decide to make \"git\nrefs list\" a wrapper for \"git for-each-ref\", we need to change the\ncurrent code design as we introduced repeated code path.\n\n---\n\nSorry for the late reply, as I am extremely busy with my own business.\n\nThanks,\nJialuo\n"},{"id":"521670","messageId":"aG5wSB_ce5Z0BCJc@pks.im","threadId":"63703","inReplyTo":"xmqqldp9m1mr.fsf@gitster.g","subject":"Re: [GSoC][RFC PATCH 0/2] Add refs list subcommand","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-09T13:36:08Z","receivedAt":"2025-07-09T13:36:21Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Jun 30, 2025 at 01:10:36PM -0700, Junio C Hamano wrote:\n> Meet Soni <meetsoni3017@gmail.com> writes:\n> \n> > To clarify, I don't have specific options or improvements in mind right now.\n> > The idea behind mentioning them was to acknowledge that having a consolidated\n> > interface like git refs might open the door to such discussions.\n> \n> One complaint we heard a lot about Git in the past (I do not know if\n> people got used to and learned to live with, or they are still\n> complaining about the same these days) was that there were always\n> multiple ways to do related things slightly differently.\n> \n> The machinery of for-each-ref is shared by branch and tag to give\n> them feature-parity.  Adding yet another command that behaves\n> slightly differently, with the intention to make it diverge even\n> more in the future, feels going backwards.\n\nThat's fair. I do think that the common infrastructure should be shared\nindeed, and that includes the options. So the most important benefit of\nthe new subcommand would be an improvement to discoverability.\n\nBut there is one default in git-for-each-ref(1) that has been biting us\nmultiple times at GitLab already, namely the default format. In\ngit-for-each-ref(1) it is:\n\n    %(objectname) %(objecttype)\\t%(refname)\n\nThe problem with this format is `%(objecttype)` -- it requires us to not\nonly read refs from the reference database, but also resolve the object\nvia the ODB so that we can figure out its type. And that can be a huge\nslowdown in large repositories. Take e.g. the Chromium repository:\n\n    Benchmark 1: git for-each-ref\n      Time (mean ± σ):     148.9 ms ±   1.0 ms    [User: 80.7 ms, System: 67.2 ms]\n      Range (min … max):   147.2 ms … 154.2 ms    100 runs\n\n    Benchmark 2: git for-each-ref --format=\"%(objectname) %(refname)\"\n      Time (mean ± σ):      22.4 ms ±   0.3 ms    [User: 21.4 ms, System: 1.0 ms]\n      Range (min … max):    22.0 ms …  24.0 ms    100 runs\n\n    Summary\n      git for-each-ref --format=\"%(objectname) %(refname)\" ran\n        6.65 ± 0.09 times faster than git for-each-ref\n\nSo from my point of view, this is a thing we should consider changing in\nthe new subcommand. For all the other options I agree, we should have\ncommon infra and thus common options.\n\nPatrick\n"},{"id":"522172","messageId":"20250717075009.26262-1-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250627074934.1761897-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v2 0/2] Add refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-17T07:50:07Z","receivedAt":"2025-07-17T07:50:27Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Hello everyone,\n\nThis is the second version of the patch series that introduces the `git\nrefs list` subcommand as a modern alternative to `git for-each-ref`.\n\nThank you to everyone who provided valuable feedback on the first\nversion. This new version incorporates the suggestions, improving both\nthe code's implementation and the documentation's structure.\n\nChanges in v2:\n  - Refactored the implementation of `refs list`. Instead of duplicating\n    the logic from `for-each-ref`, it is now a thin wrapper that\n    calls the original command. This avoids code duplication while\n    maintaining identical behavior.\n\n  - Refactored the documentation by moving the common command options\n    shared by `refs list` and `for-each-ref` into a new standalone\n    file (`ref-list-options.adoc`).\n\n  - Both man pages now use the AsciiDoc `include::` macro to embed these\n    shared options, ensuring documentation stays consistent.\n\n---\n\n(v1 cover-letter text)\n\nThis patch series introduces `git refs list` as a modern replacement for\n`git for-each-ref`, as part of an effort to consolidate ref-related\nfunctionality under a unified `git refs` command.\n\nGit's ref-related operations are currently handled by several distinct\ncommands, such as `git show-ref`, `git for-each-ref`, `git update-ref`,\n`git pack-refs`, etc. This distribution has a few practical drawbacks:\n\n- Users need to rely on multiple commands for related tasks involving\n  refs.\n\n- The commands may differ slightly in behavior and option syntax,\n  leading to inconsistency.\n\nWe propose a long-term consolidation effort to bring ref-related\nsubcommands under the umbrella of a single command: `git refs`.\n\nThe implementation of `git refs list` is functionally identical to `git\nfor-each-ref`. It reuses the same internal logic (cmd_for_each_ref) to\nensure complete backward compatibility. The purpose of this patch is not\nto introduce new behavior but to provide an alternate entry point under\nthe consolidated `git refs` namespace.\n\nThe motivation behind this change is twofold:\n\n- Consolidation: Centralizing ref-related operations makes them easier\n  to discover, use, and maintain.\n\n- Evolution: While the initial goal is parity with existing commands,\n  this consolidation allows for careful reconsideration of which\n  features are essential. Over time, we can:\n\n  - Remove legacy or obscure options that are no longer needed.\n  - Add improvements that wouldn't make sense to bolt onto legacy\n    commands.\n  - Offering a more consistent and user-friendly surface.\n\nTo verify backward compatibility, this patch also includes a test\n`t/t1461-refs-list.sh`, which runs the full `t6300-for-each-ref.sh` test\nusing `git refs list`. The test uses ${GIT_REFS_LIST_CMD:-for-each-ref}\nto allow substitution without duplicating tests.\n\nThis patch is deliberately conservative: it introduces no behavioral\nchanges and leaves `for-each-ref` untouched. The goal is to lay\ngroundwork and demonstrate viability of ref consolidation within `git\nrefs`.\n\nGoing forward, I'd like to initiate a discussion on what the ideal\nsurface of `git refs list` should look like. Which options and features\nfrom `for-each-ref` should be carried over? Are there any that are\nobsolete or overly niche? What improvements might be worth considering\nnow that we have a new, consolidated interface?\n\nFeedback on this, especially from those who rely on `for-each-ref` in\nscripts or tooling would be very helpful.\n\nMeet Soni (2):\n  builtin/refs: add list subcommand\n  t: add test for git refs list subcommand\n\n Documentation/git-for-each-ref.adoc  |  80 +------\n Documentation/git-refs.adoc          |  16 ++\n Documentation/refs-list-options.adoc |  80 +++++++\n builtin/for-each-ref.c               |  24 +-\n builtin/refs.c                       |  35 +++\n t/meson.build                        |   1 +\n t/t1461-refs-list.sh                 |   8 +\n t/t6300-for-each-ref.sh              | 333 ++++++++++++++-------------\n 8 files changed, 331 insertions(+), 246 deletions(-)\n create mode 100644 Documentation/refs-list-options.adoc\n create mode 100755 t/t1461-refs-list.sh\n\nRange-diff against v1:\n1:  e7b19c71eb ! 1:  b2d3026520 builtin/refs: add list subcommand\n    @@ Commit message\n         instead of duplicating its logic. Forward all arguments to the existing\n         function to ensure behavior is identical.\n     \n    -    This prevents code duplication and allows `refs list` to benefit from\n    -    any future fixes to the underlying `for-each-ref` machinery.\n    +    Add documentation for the new command. To keep the documentation DRY and\n    +    consistent with `git-for-each-ref(1)`, refactor the shared command\n    +    options into a standalone file. Use the AsciiDoc `include::` macro to\n    +    embed these options in both man pages.\n    +\n    +    This prevents duplication in both code and documentation, ensuring that\n    +    `refs list` benefits from any future fixes to the underlying\n    +    `for-each-ref` machinery and its shared documentation.\n     \n         Mentored-by: Patrick Steinhardt <ps@pks.im>\n         Mentored-by: shejialuo <shejialuo@gmail.com>\n         Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n         Signed-off-by: Meet Soni <meetsoni3017@gmail.com>\n     \n    + ## Documentation/git-for-each-ref.adoc ##\n    +@@ Documentation/git-for-each-ref.adoc: host language allowing their direct evaluation in that language.\n    + \n    + OPTIONS\n    + -------\n    +-<pattern>...::\n    +-\tIf one or more patterns are given, only refs are shown that\n    +-\tmatch against at least one pattern, either using fnmatch(3) or\n    +-\tliterally, in the latter case matching completely or from the\n    +-\tbeginning up to a slash.\n    +-\n    +---stdin::\n    +-\tIf `--stdin` is supplied, then the list of patterns is read from\n    +-\tstandard input instead of from the argument list.\n    +-\n    +---count=<count>::\n    +-\tBy default the command shows all refs that match\n    +-\t`<pattern>`.  This option makes it stop after showing\n    +-\tthat many refs.\n    +-\n    +---sort=<key>::\n    +-\tA field name to sort on.  Prefix `-` to sort in\n    +-\tdescending order of the value.  When unspecified,\n    +-\t`refname` is used.  You may use the --sort=<key> option\n    +-\tmultiple times, in which case the last key becomes the primary\n    +-\tkey.\n    +-\n    +---format=<format>::\n    +-\tA string that interpolates `%(fieldname)` from a ref being shown and\n    +-\tthe object it points at. In addition, the string literal `%%`\n    +-\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n    +-\tthe character with hex code `xx`. For example, `%00` interpolates to\n    +-\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n    +-+\n    +-When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n    +-TAB %(refname)`.\n    +-\n    +---color[=<when>]::\n    +-\tRespect any colors specified in the `--format` option. The\n    +-\t`<when>` field must be one of `always`, `never`, or `auto` (if\n    +-\t`<when>` is absent, behave as if `always` was given).\n    +-\n    +---shell::\n    +---perl::\n    +---python::\n    +---tcl::\n    +-\tIf given, strings that substitute `%(fieldname)`\n    +-\tplaceholders are quoted as string literals suitable for\n    +-\tthe specified host language.  This is meant to produce\n    +-\ta scriptlet that can directly be `eval`ed.\n    +-\n    +---points-at=<object>::\n    +-\tOnly list refs which points at the given object.\n    +-\n    +---merged[=<object>]::\n    +-\tOnly list refs whose tips are reachable from the\n    +-\tspecified commit (HEAD if not specified).\n    +-\n    +---no-merged[=<object>]::\n    +-\tOnly list refs whose tips are not reachable from the\n    +-\tspecified commit (HEAD if not specified).\n    +-\n    +---contains[=<object>]::\n    +-\tOnly list refs which contain the specified commit (HEAD if not\n    +-\tspecified).\n    +-\n    +---no-contains[=<object>]::\n    +-\tOnly list refs which don't contain the specified commit (HEAD\n    +-\tif not specified).\n    +-\n    +---ignore-case::\n    +-\tSorting and filtering refs are case insensitive.\n    +-\n    +---omit-empty::\n    +-\tDo not print a newline after formatted refs where the format expands\n    +-\tto the empty string.\n    +-\n    +---exclude=<pattern>::\n    +-\tIf one or more patterns are given, only refs which do not match\n    +-\tany excluded pattern(s) are shown. Matching is done using the\n    +-\tsame rules as `<pattern>` above.\n    +-\n    +---include-root-refs::\n    +-\tList root refs (HEAD and pseudorefs) apart from regular refs.\n    ++include::refs-list-options.adoc[]\n    + \n    + FIELD NAMES\n    + -----------\n    +\n      ## Documentation/git-refs.adoc ##\n     @@ Documentation/git-refs.adoc: SYNOPSIS\n      [synopsis]\n    @@ Documentation/git-refs.adoc: migrate::\n      \tVerify reference database consistency.\n      \n     +list::\n    -+\tList references in the repository with support for filtering, formatting,\n    -+\tand sorting. This subcommand uses the same core logic as\n    -+\tlinkgit:git-for-each-ref[1] and offers equivalent functionality.\n    ++\tList references in the repository with support for filtering,\n    ++\tformatting, and sorting. This subcommand is an alias for\n    ++\tlinkgit:git-for-each-ref[1] and offers identical functionality.\n     +\n      OPTIONS\n      -------\n    @@ Documentation/git-refs.adoc: The following options are specific to 'git refs ver\n      \n     +The following options are specific to 'git refs list':\n     +\n    ++include::refs-list-options.adoc[]\n    ++\n    + KNOWN LIMITATIONS\n    + -----------------\n    + \n    +\n    + ## Documentation/refs-list-options.adoc (new) ##\n    +@@\n    ++// Shared options for for-each-ref and refs list\n     +<pattern>...::\n     +\tIf one or more patterns are given, only refs are shown that\n     +\tmatch against at least one pattern, either using fnmatch(3) or\n    @@ Documentation/git-refs.adoc: The following options are specific to 'git refs ver\n     +\n     +--include-root-refs::\n     +\tList root refs (HEAD and pseudorefs) apart from regular refs.\n    -+\n    -+\n    - KNOWN LIMITATIONS\n    - -----------------\n    - \n     \n      ## builtin/for-each-ref.c ##\n     @@ builtin/for-each-ref.c: static char const * const for_each_ref_usage[] = {\n2:  8ce521880c = 2:  2d6534841f t: add test for git refs list subcommand\n-- \n2.34.1\n\n"},{"id":"522173","messageId":"20250717075009.26262-2-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250717075009.26262-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v2 1/2] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-17T07:50:08Z","receivedAt":"2025-07-17T07:50:31Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Git's reference management is distributed across multiple commands. As\npart of an ongoing effort to consolidate and modernize reference\nhandling, introduce a `list` subcommand under the `git refs` umbrella as\na replacement for `git for-each-ref`.\n\nImplement `cmd_refs_list` as a thin wrapper around `cmd_for_each_ref`\ninstead of duplicating its logic. Forward all arguments to the existing\nfunction to ensure behavior is identical.\n\nAdd documentation for the new command. To keep the documentation DRY and\nconsistent with `git-for-each-ref(1)`, refactor the shared command\noptions into a standalone file. Use the AsciiDoc `include::` macro to\nembed these options in both man pages.\n\nThis prevents duplication in both code and documentation, ensuring that\n`refs list` benefits from any future fixes to the underlying\n`for-each-ref` machinery and its shared documentation.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/git-for-each-ref.adoc  | 80 +---------------------------\n Documentation/git-refs.adoc          | 16 ++++++\n Documentation/refs-list-options.adoc | 80 ++++++++++++++++++++++++++++\n builtin/for-each-ref.c               | 24 +++++++--\n builtin/refs.c                       | 35 ++++++++++++\n 5 files changed, 152 insertions(+), 83 deletions(-)\n create mode 100644 Documentation/refs-list-options.adoc\n\ndiff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\nindex 5ef89fc0fe..f7bbc1902a 100644\n--- a/Documentation/git-for-each-ref.adoc\n+++ b/Documentation/git-for-each-ref.adoc\n@@ -28,85 +28,7 @@ host language allowing their direct evaluation in that language.\n \n OPTIONS\n -------\n-<pattern>...::\n-\tIf one or more patterns are given, only refs are shown that\n-\tmatch against at least one pattern, either using fnmatch(3) or\n-\tliterally, in the latter case matching completely or from the\n-\tbeginning up to a slash.\n-\n---stdin::\n-\tIf `--stdin` is supplied, then the list of patterns is read from\n-\tstandard input instead of from the argument list.\n-\n---count=<count>::\n-\tBy default the command shows all refs that match\n-\t`<pattern>`.  This option makes it stop after showing\n-\tthat many refs.\n-\n---sort=<key>::\n-\tA field name to sort on.  Prefix `-` to sort in\n-\tdescending order of the value.  When unspecified,\n-\t`refname` is used.  You may use the --sort=<key> option\n-\tmultiple times, in which case the last key becomes the primary\n-\tkey.\n-\n---format=<format>::\n-\tA string that interpolates `%(fieldname)` from a ref being shown and\n-\tthe object it points at. In addition, the string literal `%%`\n-\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n-\tthe character with hex code `xx`. For example, `%00` interpolates to\n-\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n-+\n-When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n-TAB %(refname)`.\n-\n---color[=<when>]::\n-\tRespect any colors specified in the `--format` option. The\n-\t`<when>` field must be one of `always`, `never`, or `auto` (if\n-\t`<when>` is absent, behave as if `always` was given).\n-\n---shell::\n---perl::\n---python::\n---tcl::\n-\tIf given, strings that substitute `%(fieldname)`\n-\tplaceholders are quoted as string literals suitable for\n-\tthe specified host language.  This is meant to produce\n-\ta scriptlet that can directly be `eval`ed.\n-\n---points-at=<object>::\n-\tOnly list refs which points at the given object.\n-\n---merged[=<object>]::\n-\tOnly list refs whose tips are reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---no-merged[=<object>]::\n-\tOnly list refs whose tips are not reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---contains[=<object>]::\n-\tOnly list refs which contain the specified commit (HEAD if not\n-\tspecified).\n-\n---no-contains[=<object>]::\n-\tOnly list refs which don't contain the specified commit (HEAD\n-\tif not specified).\n-\n---ignore-case::\n-\tSorting and filtering refs are case insensitive.\n-\n---omit-empty::\n-\tDo not print a newline after formatted refs where the format expands\n-\tto the empty string.\n-\n---exclude=<pattern>::\n-\tIf one or more patterns are given, only refs which do not match\n-\tany excluded pattern(s) are shown. Matching is done using the\n-\tsame rules as `<pattern>` above.\n-\n---include-root-refs::\n-\tList root refs (HEAD and pseudorefs) apart from regular refs.\n+include::refs-list-options.adoc[]\n \n FIELD NAMES\n -----------\ndiff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\nindex 4d6dc994f9..ded90f435b 100644\n--- a/Documentation/git-refs.adoc\n+++ b/Documentation/git-refs.adoc\n@@ -11,6 +11,13 @@ SYNOPSIS\n [synopsis]\n git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n git refs verify [--strict] [--verbose]\n+git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n+\t      [(--sort=<key>)...] [--format=<format>]\n+\t      [--include-root-refs] [ --stdin | <pattern>... ]\n+\t      [--points-at=<object>]\n+\t      [--merged[=<object>]] [--no-merged[=<object>]]\n+\t      [--contains[=<object>]] [--no-contains[=<object>]]\n+\t      [--exclude=<pattern> ...]\n \n DESCRIPTION\n -----------\n@@ -26,6 +33,11 @@ migrate::\n verify::\n \tVerify reference database consistency.\n \n+list::\n+\tList references in the repository with support for filtering,\n+\tformatting, and sorting. This subcommand is an alias for\n+\tlinkgit:git-for-each-ref[1] and offers identical functionality.\n+\n OPTIONS\n -------\n \n@@ -57,6 +69,10 @@ The following options are specific to 'git refs verify':\n --verbose::\n \tWhen verifying the reference database consistency, be chatty.\n \n+The following options are specific to 'git refs list':\n+\n+include::refs-list-options.adoc[]\n+\n KNOWN LIMITATIONS\n -----------------\n \ndiff --git a/Documentation/refs-list-options.adoc b/Documentation/refs-list-options.adoc\nnew file mode 100644\nindex 0000000000..9d6557cdb9\n--- /dev/null\n+++ b/Documentation/refs-list-options.adoc\n@@ -0,0 +1,80 @@\n+// Shared options for for-each-ref and refs list\n+<pattern>...::\n+\tIf one or more patterns are given, only refs are shown that\n+\tmatch against at least one pattern, either using fnmatch(3) or\n+\tliterally, in the latter case matching completely or from the\n+\tbeginning up to a slash.\n+\n+--stdin::\n+\tIf `--stdin` is supplied, then the list of patterns is read from\n+\tstandard input instead of from the argument list.\n+\n+--count=<count>::\n+\tBy default the command shows all refs that match\n+\t`<pattern>`.  This option makes it stop after showing\n+\tthat many refs.\n+\n+--sort=<key>::\n+\tA field name to sort on.  Prefix `-` to sort in\n+\tdescending order of the value.  When unspecified,\n+\t`refname` is used.  You may use the --sort=<key> option\n+\tmultiple times, in which case the last key becomes the primary\n+\tkey.\n+\n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from a ref being shown and\n+\tthe object it points at. In addition, the string literal `%%`\n+\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n+\tthe character with hex code `xx`. For example, `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n++\n+When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n+TAB %(refname)`.\n+\n+--color[=<when>]::\n+\tRespect any colors specified in the `--format` option. The\n+\t`<when>` field must be one of `always`, `never`, or `auto` (if\n+\t`<when>` is absent, behave as if `always` was given).\n+\n+--shell::\n+--perl::\n+--python::\n+--tcl::\n+\tIf given, strings that substitute `%(fieldname)`\n+\tplaceholders are quoted as string literals suitable for\n+\tthe specified host language.  This is meant to produce\n+\ta scriptlet that can directly be `eval`ed.\n+\n+--points-at=<object>::\n+\tOnly list refs which points at the given object.\n+\n+--merged[=<object>]::\n+\tOnly list refs whose tips are reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--no-merged[=<object>]::\n+\tOnly list refs whose tips are not reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--contains[=<object>]::\n+\tOnly list refs which contain the specified commit (HEAD if not\n+\tspecified).\n+\n+--no-contains[=<object>]::\n+\tOnly list refs which don't contain the specified commit (HEAD\n+\tif not specified).\n+\n+--ignore-case::\n+\tSorting and filtering refs are case insensitive.\n+\n+--omit-empty::\n+\tDo not print a newline after formatted refs where the format expands\n+\tto the empty string.\n+\n+--exclude=<pattern>::\n+\tIf one or more patterns are given, only refs which do not match\n+\tany excluded pattern(s) are shown. Matching is done using the\n+\tsame rules as `<pattern>` above.\n+\n+--include-root-refs::\n+\tList root refs (HEAD and pseudorefs) apart from regular refs.\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 3d2207ec77..d7d8279049 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -16,11 +16,27 @@ static char const * const for_each_ref_usage[] = {\n \tNULL\n };\n \n+#define REFS_LIST_USAGE \\\n+\tN_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t   \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n+\t   \"              [--points-at=<object>]\\n\" \\\n+\t   \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t   \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t   \"              [--exclude=<pattern> ...]\")\n+\n+static char const * const refs_list_usage[] = {\n+\tREFS_LIST_USAGE,\n+\tNULL\n+};\n+\n int cmd_for_each_ref(int argc,\n \t\t     const char **argv,\n \t\t     const char *prefix,\n \t\t     struct repository *repo)\n {\n+\tint cmd_is_refs_list = !strcmp(argv[0], \"refs list\");\n+\tconst char *const *opt_usage = cmd_is_refs_list ? refs_list_usage : for_each_ref_usage;\n \tstruct ref_sorting *sorting;\n \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n \tint icase = 0, include_root_refs = 0, from_stdin = 0;\n@@ -67,17 +83,17 @@ int cmd_for_each_ref(int argc,\n \t/* Set default (refname) sorting */\n \tstring_list_append(&sorting_options, \"refname\");\n \n-\tparse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);\n+\tparse_options(argc, argv, prefix, opts, opt_usage, 0);\n \tif (format.array_opts.max_count < 0) {\n \t\terror(\"invalid --count argument: `%d'\", format.array_opts.max_count);\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(opt_usage, opts);\n \t}\n \tif (HAS_MULTI_BITS(format.quote_style)) {\n \t\terror(\"more than one quoting style?\");\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(opt_usage, opts);\n \t}\n \tif (verify_ref_format(&format))\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(opt_usage, opts);\n \n \tsorting = ref_sorting_options(&sorting_options);\n \tref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);\ndiff --git a/builtin/refs.c b/builtin/refs.c\nindex 998d2a2c1c..41e29d1b5f 100644\n--- a/builtin/refs.c\n+++ b/builtin/refs.c\n@@ -3,6 +3,7 @@\n #include \"config.h\"\n #include \"fsck.h\"\n #include \"parse-options.h\"\n+#include \"ref-filter.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n #include \"worktree.h\"\n@@ -13,6 +14,15 @@\n #define REFS_VERIFY_USAGE \\\n \tN_(\"git refs verify [--strict] [--verbose]\")\n \n+#define REFS_LIST_USAGE \\\n+\tN_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t   \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n+\t   \"              [--points-at=<object>]\\n\" \\\n+\t   \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t   \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t   \"              [--exclude=<pattern> ...]\")\n+\n static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,\n \t\t\t    struct repository *repo UNUSED)\n {\n@@ -101,6 +111,29 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,\n \treturn ret;\n }\n \n+static int cmd_refs_list(int argc, const char **argv, const char *prefix,\n+\t\t  struct repository *repo)\n+{\n+\tstruct strvec args = STRVEC_INIT;\n+\tconst char **args_copy;\n+\tint ret;\n+\n+\tstrvec_push(&args, \"refs list\");\n+\n+\tfor (int i = 1; i < argc; i++)\n+\t\tstrvec_push(&args, argv[i]);\n+\n+\tCALLOC_ARRAY(args_copy, args.nr + 1);\n+\tCOPY_ARRAY(args_copy, args.v, args.nr);\n+\n+\tret = cmd_for_each_ref(args.nr, args_copy, prefix, repo);\n+\n+\tstrvec_clear(&args);\n+\tfree(args_copy);\n+\n+\treturn ret;\n+}\n+\n int cmd_refs(int argc,\n \t     const char **argv,\n \t     const char *prefix,\n@@ -109,12 +142,14 @@ int cmd_refs(int argc,\n \tconst char * const refs_usage[] = {\n \t\tREFS_MIGRATE_USAGE,\n \t\tREFS_VERIFY_USAGE,\n+\t\tREFS_LIST_USAGE,\n \t\tNULL,\n \t};\n \tparse_opt_subcommand_fn *fn = NULL;\n \tstruct option opts[] = {\n \t\tOPT_SUBCOMMAND(\"migrate\", &fn, cmd_refs_migrate),\n \t\tOPT_SUBCOMMAND(\"verify\", &fn, cmd_refs_verify),\n+\t\tOPT_SUBCOMMAND(\"list\", &fn, cmd_refs_list),\n \t\tOPT_END(),\n \t};\n \n-- \n2.34.1\n\n"},{"id":"522174","messageId":"20250717075009.26262-3-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250717075009.26262-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v2 2/2] t: add test for git refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-17T07:50:09Z","receivedAt":"2025-07-17T07:50:36Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Adapt `t/t6300-for-each-ref.sh` to invoke ${GIT_REFS_LIST_CMD},\ndefaulting to `for-each-ref` if the variable is unset.\n\nAdd `t/t1461-refs-list.sh` to test git refs list by setting\n`GIT_REFS_LIST_CMD` to `refs list` and sourcing\n`t/t6300-for-each-ref.sh`\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/meson.build           |   1 +\n t/t1461-refs-list.sh    |   8 +\n t/t6300-for-each-ref.sh | 333 ++++++++++++++++++++--------------------\n 3 files changed, 179 insertions(+), 163 deletions(-)\n create mode 100755 t/t1461-refs-list.sh\n\ndiff --git a/t/meson.build b/t/meson.build\nindex 50e89e764a..c959c039d0 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -224,6 +224,7 @@ integration_tests = [\n   't1450-fsck.sh',\n   't1451-fsck-buffer.sh',\n   't1460-refs-migrate.sh',\n+  't1461-refs-list.sh',\n   't1500-rev-parse.sh',\n   't1501-work-tree.sh',\n   't1502-rev-parse-parseopt.sh',\ndiff --git a/t/t1461-refs-list.sh b/t/t1461-refs-list.sh\nnew file mode 100755\nindex 0000000000..9def7b2e5b\n--- /dev/null\n+++ b/t/t1461-refs-list.sh\n@@ -0,0 +1,8 @@\n+#!/bin/sh\n+\n+test_description='git refs list tests'\n+\n+. ./test-lib.sh\n+\n+GIT_REFS_LIST_CMD='refs list'\n+. \"$TEST_DIRECTORY\"/t6300-for-each-ref.sh\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex ce9af79ab1..74a030371c 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -5,7 +5,9 @@\n \n test_description='for-each-ref test'\n \n-. ./test-lib.sh\n+. \"${TEST_DIRECTORY:-.}/test-lib.sh\"\n+\n+GIT_REFS_LIST_CMD=${GIT_REFS_LIST_CMD:-for-each-ref}\n GNUPGHOME_NOT_USED=$GNUPGHOME\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n . \"$TEST_DIRECTORY\"/lib-terminal.sh\n@@ -61,7 +63,7 @@ test_atom () {\n \n \tprintf '%s\\n' \"$3\" >expected\n \t$test_do $PREREQ \"basic atom: $ref $format\" '\n-\t\tgit for-each-ref --format=\"%($format)\" \"$ref\" >actual &&\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%($format)\" \"$ref\" >actual &&\n \t\tsanitize_pgp <actual >actual.clean &&\n \t\ttest_cmp expected actual.clean\n \t'\n@@ -88,7 +90,7 @@ test_atom () {\n \t\t\tesac &&\n \t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n \t\t\techo $expect >expected &&\n-\t\t\tgit for-each-ref --format=\"%(contents:size)\" \"$ref\" >actual &&\n+\t\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(contents:size)\" \"$ref\" >actual &&\n \t\t\ttest_cmp expected actual\n \t\t'\n \tfi\n@@ -281,7 +283,7 @@ test_atom tag HEAD ' '\n \n test_expect_success 'basic atom: refs/tags/testtag *raw' '\n \tgit cat-file commit refs/tags/testtag^{} >expected &&\n-\tgit for-each-ref --format=\"%(*raw)\" refs/tags/testtag >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(*raw)\" refs/tags/testtag >actual &&\n \tsanitize_pgp <expected >expected.clean &&\n \techo >>expected.clean &&\n \tsanitize_pgp <actual >actual.clean &&\n@@ -289,40 +291,45 @@ test_expect_success 'basic atom: refs/tags/testtag *raw' '\n '\n \n test_expect_success 'Check invalid atoms names are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'for-each-ref does not crash with -h' '\n-\ttest_expect_code 129 git for-each-ref -h >usage &&\n-\ttest_grep \"[Uu]sage: git for-each-ref \" usage &&\n-\ttest_expect_code 129 nongit git for-each-ref -h >usage &&\n-\ttest_grep \"[Uu]sage: git for-each-ref \" usage\n-'\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(INVALID)\" refs/heads\n+'\n+\n+if test \"$GIT_REFS_LIST_CMD\" = \"refs list\"\n+then\n+\tsay \"Skipping -h crash test for git refs list\"\n+else\n+\ttest_expect_success \"${GIT_REFS_LIST_CMD} does not crash with -h\" '\n+\t\ttest_expect_code 129 git ${GIT_REFS_LIST_CMD} -h >usage &&\n+\t\ttest_grep \"[Uu]sage: git for-each-ref \" usage &&\n+\t\ttest_expect_code 129 nongit git ${GIT_REFS_LIST_CMD} -h >usage &&\n+\t\ttest_grep \"[Uu]sage: git for-each-ref \" usage\n+\t'\n+fi\n \n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n-\tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n '\n \n test_expect_success 'Check valid format specifiers for date fields' '\n-\tgit for-each-ref --format=\"%(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:relative)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:short)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:local)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:iso8601)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:rfc2822)\" refs/heads\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:default)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:relative)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:short)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:local)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:iso8601)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:rfc2822)\" refs/heads\n '\n \n test_expect_success 'Check invalid format specifiers are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(authordate:INVALID)\" refs/heads\n '\n \n test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(objectname:short=0)\" &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(objectname:short=-1)\" &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(objectname:short=foo)\"\n '\n \n test_bad_atom () {\n@@ -337,7 +344,7 @@ test_bad_atom () {\n \n \tprintf '%s\\n' \"$3\" >expect\n \t$test_do $PREREQ \"err basic atom: $ref $format\" '\n-\t\ttest_must_fail git for-each-ref \\\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} \\\n \t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n \t\ttest_cmp expect error\n \t'\n@@ -395,10 +402,10 @@ test_date () {\n \t'refs/tags/testtag' '$tagger_date'\n \tEOF\n \t(\n-\t\tgit for-each-ref --shell \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --shell \\\n \t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n \t\t\trefs/heads &&\n-\t\tgit for-each-ref --shell \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --shell \\\n \t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n \t\t\trefs/tags\n \t) >actual &&\n@@ -429,16 +436,16 @@ test_expect_success 'Check format \"default-local\" date fields output' '\n # doesn't exit in error\n test_expect_success 'Check format \"relative\" date fields output' '\n \tf=relative &&\n-\t(git for-each-ref --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n-\tgit for-each-ref --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n+\t(git ${GIT_REFS_LIST_CMD} --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n+\tgit ${GIT_REFS_LIST_CMD} --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n '\n \n # We just check that this is the same as \"relative\" for now.\n test_expect_success 'Check format \"relative-local\" date fields output' '\n \ttest_date relative-local \\\n-\t\t\"$(git for-each-ref --format=\"%(committerdate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(authordate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(taggerdate:relative)\" refs/tags)\"\n+\t\t\"$(git ${GIT_REFS_LIST_CMD} --format=\"%(committerdate:relative)\" refs/heads)\" \\\n+\t\t\"$(git ${GIT_REFS_LIST_CMD} --format=\"%(authordate:relative)\" refs/heads)\" \\\n+\t\t\"$(git ${GIT_REFS_LIST_CMD} --format=\"%(taggerdate:relative)\" refs/tags)\"\n '\n \n test_expect_success 'Check format \"short\" date fields output' '\n@@ -488,7 +495,7 @@ test_expect_success 'Check format \"raw-local\" date fields output' '\n \n test_expect_success 'Check format of strftime date fields' '\n \techo \"my date is 2006-07-04\" >expected &&\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n \t  refs/heads >actual &&\n \ttest_cmp expected actual\n@@ -496,7 +503,7 @@ test_expect_success 'Check format of strftime date fields' '\n \n test_expect_success 'Check format of strftime-local date fields' '\n \techo \"my date is 2006-07-03\" >expected &&\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n \t  refs/heads >actual &&\n \ttest_cmp expected actual\n@@ -504,11 +511,11 @@ test_expect_success 'Check format of strftime-local date fields' '\n \n test_expect_success 'exercise strftime with odd fields' '\n \techo >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:)\" refs/heads >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:format:)\" refs/heads >actual &&\n \ttest_cmp expected actual &&\n \tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n \techo $long >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -519,7 +526,7 @@ refs/tags/testtag\n EOF\n \n test_expect_success 'Verify ascending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=refname >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --sort=refname >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -531,13 +538,13 @@ refs/heads/main\n EOF\n \n test_expect_success 'Verify descending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=-refname >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --sort=-refname >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'Give help even with invalid sort atoms' '\n-\ttest_expect_code 129 git for-each-ref --sort=bogus -h >actual 2>&1 &&\n-\tgrep \"^usage: git for-each-ref\" actual\n+\ttest_expect_code 129 git ${GIT_REFS_LIST_CMD} --sort=bogus -h >actual 2>&1 &&\n+\tgrep \"^usage: git ${GIT_REFS_LIST_CMD}\" actual\n '\n \n cat >expected <<\\EOF\n@@ -548,7 +555,7 @@ EOF\n test_expect_success 'exercise patterns with prefixes' '\n \tgit tag testtag-2 &&\n \ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n \ttest_cmp expected actual\n '\n@@ -561,7 +568,7 @@ EOF\n test_expect_success 'exercise glob patterns with prefixes' '\n \tgit tag testtag-2 &&\n \ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n \ttest_cmp expected actual\n '\n@@ -578,7 +585,7 @@ test_expect_success 'exercise patterns with prefix exclusions' '\n \t\tgit tag \"$tag\" || return 1\n \tdone &&\n \ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n \ttest_cmp expected actual\n '\n@@ -596,7 +603,7 @@ test_expect_success 'exercise patterns with pattern exclusions' '\n \t\tgit tag \"$tag\" || return 1\n \tdone &&\n \ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n \ttest_cmp expected actual\n '\n@@ -608,17 +615,17 @@ cat >expected <<\\EOF\n EOF\n \n test_expect_success 'Quoting style: shell' '\n-\tgit for-each-ref --shell --format=\"%(refname)\" >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --shell --format=\"%(refname)\" >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'Quoting style: perl' '\n-\tgit for-each-ref --perl --format=\"%(refname)\" >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --perl --format=\"%(refname)\" >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'Quoting style: python' '\n-\tgit for-each-ref --python --format=\"%(refname)\" >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --python --format=\"%(refname)\" >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -629,13 +636,13 @@ cat >expected <<\\EOF\n EOF\n \n test_expect_success 'Quoting style: tcl' '\n-\tgit for-each-ref --tcl --format=\"%(refname)\" >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --tcl --format=\"%(refname)\" >actual &&\n \ttest_cmp expected actual\n '\n \n for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n \ttest_expect_success \"more than one quoting style: $i\" \"\n-\t\ttest_must_fail git for-each-ref $i 2>err &&\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} $i 2>err &&\n \t\tgrep '^error: more than one quoting style' err\n \t\"\n done\n@@ -659,8 +666,8 @@ test_atom head push:track '[behind 1]'\n test_atom head push:trackshort '<'\n \n test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:track)\" 2>/dev/null &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:trackshort)\" 2>/dev/null\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname:track)\" 2>/dev/null &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname:trackshort)\" 2>/dev/null\n '\n \n test_expect_success 'Check that :track[short] works when upstream is invalid' '\n@@ -670,14 +677,14 @@ test_expect_success 'Check that :track[short] works when upstream is invalid' '\n \tEOF\n \ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n \tgit config branch.main.merge refs/heads/does-not-exist &&\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n \t\trefs/heads >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'Check for invalid refname format' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname:INVALID)\"\n '\n \n test_expect_success 'set up color tests' '\n@@ -694,24 +701,24 @@ test_expect_success 'set up color tests' '\n '\n \n test_expect_success TTY '%(color) shows color with a tty' '\n-\ttest_terminal git for-each-ref --format=\"$color_format\" >actual.raw &&\n+\ttest_terminal git ${GIT_REFS_LIST_CMD} --format=\"$color_format\" >actual.raw &&\n \ttest_decode_color <actual.raw >actual &&\n \ttest_cmp expected.color actual\n '\n \n test_expect_success '%(color) does not show color without tty' '\n-\tTERM=vt100 git for-each-ref --format=\"$color_format\" >actual &&\n+\tTERM=vt100 git ${GIT_REFS_LIST_CMD} --format=\"$color_format\" >actual &&\n \ttest_cmp expected.bare actual\n '\n \n test_expect_success '--color can override tty check' '\n-\tgit for-each-ref --color --format=\"$color_format\" >actual.raw &&\n+\tgit ${GIT_REFS_LIST_CMD} --color --format=\"$color_format\" >actual.raw &&\n \ttest_decode_color <actual.raw >actual &&\n \ttest_cmp expected.color actual\n '\n \n test_expect_success 'color.ui=always does not override tty check' '\n-\tgit -c color.ui=always for-each-ref --format=\"$color_format\" >actual &&\n+\tgit -c color.ui=always ${GIT_REFS_LIST_CMD} --format=\"$color_format\" >actual &&\n \ttest_cmp expected.bare actual\n '\n \n@@ -732,7 +739,7 @@ test_expect_success 'describe atom vs git describe' '\n \t(\n \t\tcd describe-repo &&\n \n-\t\tgit for-each-ref --format=\"%(objectname)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(objectname)\" \\\n \t\t\trefs/tags/ >obj &&\n \t\twhile read hash\n \t\tdo\n@@ -747,7 +754,7 @@ test_expect_success 'describe atom vs git describe' '\n \t\ttest_path_exists expect-contains-good &&\n \t\ttest_path_exists expect-contains-bad &&\n \n-\t\tgit for-each-ref --format=\"%(objectname) %(describe)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(objectname) %(describe)\" \\\n \t\t\trefs/tags/ >actual 2>err &&\n \t\ttest_cmp expect actual &&\n \t\ttest_must_be_empty err\n@@ -758,7 +765,7 @@ test_expect_success 'describe:tags vs describe --tags' '\n \t(\n \t\tcd describe-repo &&\n \t\tgit describe --tags >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:tags)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:tags)\" \\\n \t\t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual\n \t)\n@@ -772,7 +779,7 @@ test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n \t\t#\t  recent tag reachable from it\n \t\ttest_commit --no-tag file &&\n \t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:abbrev=14)\" \\\n \t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual &&\n \n@@ -784,7 +791,7 @@ test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n \t\t#\t  the name of the tag\n \t\tgit tag -a -m tagged tagname &&\n \t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:abbrev=14)\" \\\n \t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual &&\n \t\ttest tagname = $(cat actual)\n@@ -796,7 +803,7 @@ test_expect_success 'describe:match=... vs describe --match ...' '\n \t\tcd describe-repo &&\n \t\tgit tag -a -m \"tag foo\" tag-foo &&\n \t\tgit describe --match \"*-foo\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:match=\"*-foo\")\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:match=\"*-foo\")\" \\\n \t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual\n \t)\n@@ -807,7 +814,7 @@ test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n \t\tcd describe-repo &&\n \t\tgit tag -a -m \"tag bar\" tag-bar &&\n \t\tgit describe --exclude \"*-bar\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:exclude=\"*-bar\")\" \\\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(describe:exclude=\"*-bar\")\" \\\n \t\t\trefs/heads/master >actual &&\n \t\ttest_cmp expect actual\n \t)\n@@ -824,7 +831,7 @@ test_expect_success 'deref with describe atom' '\n \n \t\ttagtwo\n \t\tEOF\n-\t\tgit for-each-ref --format=\"%(*describe)\" >actual &&\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(*describe)\" >actual &&\n \t\ttest_cmp expect actual\n \t)\n '\n@@ -837,7 +844,7 @@ test_expect_success 'err on bad describe atom arg' '\n \t\tcat >expect <<-\\EOF &&\n \t\tfatal: unrecognized %(describe) argument: baz\n \t\tEOF\n-\t\ttest_must_fail git for-each-ref --format=\"%(describe:baz)\" \\\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(describe:baz)\" \\\n \t\t\trefs/heads/master 2>actual &&\n \t\ttest_cmp expect actual &&\n \n@@ -846,7 +853,7 @@ test_expect_success 'err on bad describe atom arg' '\n \t\tcat >expect <<-\\EOF &&\n \t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n \t\tEOF\n-\t\ttest_must_fail git for-each-ref \\\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} \\\n \t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n \t\t\tref/heads/master 2>actual &&\n \t\ttest_cmp expect actual\n@@ -866,7 +873,7 @@ test_expect_success 'Check ambiguous head and tag refs (strict)' '\n \tgit commit -m \"Branch\" &&\n \tsetdate_and_increment &&\n \tgit tag -m \"Tagging at $datestamp\" main &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -877,7 +884,7 @@ EOF\n \n test_expect_success 'Check ambiguous head and tag refs (loose)' '\n \tgit config --bool core.warnambiguousrefs false &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -890,7 +897,7 @@ test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n \tgit checkout main &&\n \tgit tag ambiguous testtag^0 &&\n \tgit branch ambiguous testtag^0 &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -923,7 +930,7 @@ test_expect_success 'an unusual tag with an incomplete line' '\n \tbogo=$(git cat-file tag bogo) &&\n \tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n \tgit tag -f bogo \"$bogo\" &&\n-\tgit for-each-ref --format \"%(body)\" refs/tags/bogo\n+\tgit ${GIT_REFS_LIST_CMD} --format \"%(body)\" refs/tags/bogo\n \n '\n \n@@ -1000,7 +1007,7 @@ test_atom refs/tags/signed-empty contents \"$sig\"\n \n test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n \tgit cat-file tag refs/tags/signed-empty >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n \tsanitize_pgp <expected >expected.clean &&\n \techo >>expected.clean &&\n \tsanitize_pgp <actual >actual.clean &&\n@@ -1018,7 +1025,7 @@ $sig\"\n \n test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n \tgit cat-file tag refs/tags/signed-short >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-short >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/tags/signed-short >actual &&\n \tsanitize_pgp <expected >expected.clean &&\n \techo >>expected.clean &&\n \tsanitize_pgp <actual >actual.clean &&\n@@ -1040,7 +1047,7 @@ $sig\"\n \n test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n \tgit cat-file tag refs/tags/signed-long >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-long >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/tags/signed-long >actual &&\n \tsanitize_pgp <expected >expected.clean &&\n \techo >>expected.clean &&\n \tsanitize_pgp <actual >actual.clean &&\n@@ -1062,10 +1069,10 @@ test_atom refs/mytrees/first contents \"\"\n test_expect_success 'basic atom: refs/mytrees/first raw' '\n \tgit cat-file tree refs/mytrees/first >expected &&\n \techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/mytrees/first >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/mytrees/first >actual &&\n \ttest_cmp expected actual &&\n \tgit cat-file -s refs/mytrees/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1079,10 +1086,10 @@ test_atom refs/myblobs/first contents \"\"\n test_expect_success 'basic atom: refs/myblobs/first raw' '\n \tgit cat-file blob refs/myblobs/first >expected &&\n \techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/myblobs/first >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" refs/myblobs/first >actual &&\n \ttest_cmp expected actual &&\n \tgit cat-file -s refs/myblobs/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1127,7 +1134,7 @@ test_expect_success 'Verify sorts with raw' '\n \trefs/myblobs/blob4\n \trefs/heads/main\n \tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --sort=raw \\\n \t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n \ttest_cmp expected actual\n '\n@@ -1146,7 +1153,7 @@ test_expect_success 'Verify sorts with raw:size' '\n \trefs/mytrees/first\n \trefs/heads/main\n \tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw:size \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --sort=raw:size \\\n \t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n \ttest_cmp expected actual\n '\n@@ -1166,7 +1173,7 @@ test_expect_success 'validate raw atom with %(if:equals)' '\n \tnot equals\n \tnot equals\n \tEOF\n-\tgit for-each-ref --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n \t\trefs/myblobs/ refs/heads/ >actual &&\n \ttest_cmp expected actual\n '\n@@ -1186,7 +1193,7 @@ test_expect_success 'validate raw atom with %(if:notequals)' '\n \trefs/myblobs/blob8\n \trefs/myblobs/first\n \tEOF\n-\tgit for-each-ref --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n \t\trefs/myblobs/ refs/heads/ >actual &&\n \ttest_cmp expected actual\n '\n@@ -1203,55 +1210,55 @@ test_expect_success 'empty raw refs with %(if)' '\n \trefs/myblobs/blob8 empty\n \trefs/myblobs/first not empty\n \tEOF\n-\tgit for-each-ref --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n \t\trefs/myblobs/ >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success '%(raw) with --python must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --python\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" --python\n '\n \n test_expect_success '%(raw) with --tcl must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --tcl\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" --tcl\n '\n \n test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n \tcmp blob1 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n \tcmp blob3 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n \tcmp blob8 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n \tcmp one actual &&\n \tgit cat-file tree refs/mytrees/first > expected &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"\\$name= %(raw);\n print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n \tcmp expected actual\n '\n \n test_expect_success '%(raw) with --shell must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --shell\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" --shell\n '\n \n test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --sort=raw --shell\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(raw)\" --sort=raw --shell\n '\n \n test_expect_success '%(raw:size) with --shell' '\n-\tgit for-each-ref --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n-\tgit for-each-ref --format=\"%(raw:size)\" --shell >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(raw:size)\" --shell >actual &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'for-each-ref --format compare with cat-file --batch' '\n+test_expect_success \"${GIT_REFS_LIST_CMD} --format compare with cat-file --batch\" '\n \tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n-\tgit for-each-ref --format=\"%(objectname) %(objecttype) %(objectsize)\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(objectname) %(objecttype) %(objectsize)\n %(raw)\" refs/mytrees/first >actual &&\n \ttest_cmp expected actual\n '\n@@ -1262,7 +1269,7 @@ test_expect_success 'verify sorts with contents:size' '\n \trefs/heads/newtag\n \trefs/heads/ambiguous\n \tEOF\n-\tgit for-each-ref --format=\"%(refname)\" \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\t--sort=contents:size refs/heads/ >actual &&\n \ttest_cmp expect actual\n '\n@@ -1294,7 +1301,7 @@ test_expect_success 'Verify sort with multiple keys' '\n \t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n \t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n \tEOF\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n \t\t--sort=-refname \\\n \t\t--sort=taggeremail \\\n@@ -1314,7 +1321,7 @@ test_expect_success 'equivalent sorts fall back on refname' '\n \t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n \t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n \tEOF\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n \t\t--sort=taggerdate \\\n \t\t\"refs/tags/multi-*\" >actual &&\n@@ -1332,7 +1339,7 @@ test_expect_success '--no-sort cancels the previous sort keys' '\n \t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n \t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n \tEOF\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n \t\t--sort=-refname \\\n \t\t--sort=taggeremail \\\n@@ -1356,7 +1363,7 @@ test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n \n \t# Sort the results with `sort` for a consistent comparison against\n \t# expected\n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(refname)\" \\\n \t\t--no-sort \\\n \t\t\"refs/tags/multi-*\" | sort >actual &&\n@@ -1387,7 +1394,7 @@ test_expect_success 'sort by date defaults to full timestamp' '\n \t1707341660 refs/tags/custom-dates-1\n \tEOF\n \n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(creatordate:unix) %(refname)\" \\\n \t\t--sort=creatordate \\\n \t\t\"refs/tags/custom-dates-*\" >actual &&\n@@ -1402,7 +1409,7 @@ test_expect_success 'sort by custom date format' '\n \t21:34:20 refs/tags/custom-dates-1\n \tEOF\n \n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n \t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n \t\t\"refs/tags/custom-dates-*\" >actual &&\n@@ -1411,10 +1418,10 @@ test_expect_success 'sort by custom date format' '\n \n test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n \ttest_when_finished \"git checkout main\" &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n \tsed -e \"s/^\\* /  /\" actual >expect &&\n \tgit checkout --orphan orphaned-branch &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -1451,9 +1458,9 @@ test_trailer_option () {\n \ttitle=$1 option=$2\n \tcat >expect\n \ttest_expect_success $prereq \"$title\" '\n-\t\tgit for-each-ref --format=\"%($option)\" refs/heads/main >actual &&\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%($option)\" refs/heads/main >actual &&\n \t\ttest_cmp expect actual &&\n-\t\tgit for-each-ref --format=\"%(contents:$option)\" refs/heads/main >actual &&\n+\t\tgit ${GIT_REFS_LIST_CMD} --format=\"%(contents:$option)\" refs/heads/main >actual &&\n \t\ttest_cmp expect actual\n \t'\n }\n@@ -1583,7 +1590,7 @@ test_expect_success 'multiple %(trailers) use their own options' '\n \tEOF\n \tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n \tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n-\tgit for-each-ref --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n \tcat >expect <<-\\EOF &&\n \toneWfooXoneWbar\n \ttwoYbazZtwoYqux\n@@ -1596,9 +1603,9 @@ test_failing_trailer_option () {\n \tcat >expect\n \ttest_expect_success \"$title\" '\n \t\t# error message cannot be checked under i18n\n-\t\ttest_must_fail git for-each-ref --format=\"%($option)\" refs/heads/main 2>actual &&\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%($option)\" refs/heads/main 2>actual &&\n \t\ttest_cmp expect actual &&\n-\t\ttest_must_fail git for-each-ref --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n+\t\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n \t\ttest_cmp expect actual\n \t'\n }\n@@ -1617,14 +1624,14 @@ test_expect_success 'if arguments, %(contents:trailers) shows error if colon is\n \tcat >expect <<-EOF &&\n \tfatal: unrecognized %(contents) argument: trailersonly\n \tEOF\n-\ttest_must_fail git for-each-ref --format=\"%(contents:trailersonly)\" 2>actual &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(contents:trailersonly)\" 2>actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'basic atom: head contents:trailers' '\n-\tgit for-each-ref --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n \tsanitize_pgp <actual >actual.clean &&\n-\t# git for-each-ref ends with a blank line\n+\t# git ${GIT_REFS_LIST_CMD} ends with a blank line\n \tcat >expect <<-EOF &&\n \t$(cat trailers)\n \n@@ -1633,23 +1640,23 @@ test_expect_success 'basic atom: head contents:trailers' '\n '\n \n test_expect_success 'basic atom: rest must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(rest)\" refs/heads/main\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(rest)\" refs/heads/main\n '\n \n test_expect_success 'HEAD atom does not take arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(HEAD:foo)\" 2>err &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(HEAD:foo)\" 2>err &&\n \techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n \ttest_cmp expect err\n '\n \n test_expect_success 'subject atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(subject:foo)\" 2>err &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(subject:foo)\" 2>err &&\n \techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n \ttest_cmp expect err\n '\n \n test_expect_success 'refname atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:foo)\" 2>err &&\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname:foo)\" 2>err &&\n \techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n \ttest_cmp expect err\n '\n@@ -1673,7 +1680,7 @@ test_expect_success 'trailer parsing not fooled by --- line' '\n \t\techo \"trailer: right\" &&\n \t\techo\n \t} >expect &&\n-\tgit for-each-ref --format=\"%(trailers)\" refs/heads/main >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(trailers)\" refs/heads/main >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -1686,7 +1693,7 @@ refs/heads/main\n EOF\n \n test_expect_success 'Verify usage of %(symref) atom' '\n-\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref)\" refs/heads/sym >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1695,7 +1702,7 @@ heads/main\n EOF\n \n test_expect_success 'Verify usage of %(symref:short) atom' '\n-\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:short)\" refs/heads/sym >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1705,12 +1712,12 @@ heads/main\n EOF\n \n test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual &&\n \n-\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1720,8 +1727,8 @@ refs/heads\n EOF\n \n test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual\n '\n \n@@ -1745,38 +1752,38 @@ test_expect_success ':remotename and :remoteref' '\n \t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n \t\tdo\n \t\t\techo \"${pair#*=}\" >expect &&\n-\t\t\tgit for-each-ref --format=\"${pair%=*}\" \\\n+\t\t\tgit ${GIT_REFS_LIST_CMD} --format=\"${pair%=*}\" \\\n \t\t\t\trefs/heads/main >actual &&\n \t\t\ttest_cmp expect actual || exit 1\n \t\tdone &&\n \t\tgit branch push-simple &&\n \t\tgit config branch.push-simple.pushRemote from &&\n-\t\tactual=\"$(git for-each-ref \\\n+\t\tactual=\"$(git ${GIT_REFS_LIST_CMD} \\\n \t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n \t\t\trefs/heads/push-simple)\" &&\n \t\ttest from, = \"$actual\"\n \t)\n '\n \n-test_expect_success 'for-each-ref --ignore-case ignores case' '\n-\tgit for-each-ref --format=\"%(refname)\" refs/heads/MAIN >actual &&\n+test_expect_success \"${GIT_REFS_LIST_CMD} --ignore-case ignores case\" '\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" refs/heads/MAIN >actual &&\n \ttest_must_be_empty actual &&\n \n \techo refs/heads/main >expect &&\n-\tgit for-each-ref --format=\"%(refname)\" --ignore-case \\\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --ignore-case \\\n \t\trefs/heads/MAIN >actual &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'for-each-ref --omit-empty works' '\n-\tgit for-each-ref --format=\"%(refname)\" >actual &&\n+test_expect_success \"${GIT_REFS_LIST_CMD} --omit-empty works\" '\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" >actual &&\n \ttest_line_count -gt 1 actual &&\n-\tgit for-each-ref --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n \techo refs/heads/main >expect &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n+test_expect_success \"${GIT_REFS_LIST_CMD} --ignore-case works on multiple sort keys\" '\n \t# name refs numerically to avoid case-insensitive filesystem conflicts\n \tnr=0 &&\n \tfor email in a A b B\n@@ -1789,7 +1796,7 @@ test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n \t\t\treturn 1\n \t\tdone\n \tdone &&\n-\tgit for-each-ref --ignore-case \\\n+\tgit ${GIT_REFS_LIST_CMD} --ignore-case \\\n \t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n \t\t--sort=refname \\\n \t\t--sort=subject \\\n@@ -1816,13 +1823,13 @@ test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'for-each-ref reports broken tags' '\n+test_expect_success \"${GIT_REFS_LIST_CMD} reports broken tags\" '\n \tgit tag -m \"good tag\" broken-tag-good HEAD &&\n \tgit cat-file tag broken-tag-good >good &&\n \tsed s/commit/blob/ <good >bad &&\n \tbad=$(git hash-object -w -t tag bad) &&\n \tgit update-ref refs/tags/broken-tag-bad $bad &&\n-\ttest_must_fail git for-each-ref --format=\"%(*objectname)\" \\\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(*objectname)\" \\\n \t\trefs/tags/broken-tag-*\n '\n \n@@ -1884,21 +1891,21 @@ test_expect_success 'set up tag with signature and trailers' '\n # use \"separator=\" here to suppress the terminating newline\n test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n \n-test_expect_success 'git for-each-ref --stdin: empty' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} --stdin: empty\" '\n \t>in &&\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\tgit for-each-ref --format=\"%(refname)\" >expect &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --stdin <in >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" >expect &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'git for-each-ref --stdin: fails if extra args' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} --stdin: fails if extra args\" '\n \t>in &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname)\" \\\n+\ttest_must_fail git ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" \\\n \t\t--stdin refs/heads/extra <in 2>err &&\n \tgrep \"unknown arguments supplied with --stdin\" err\n '\n \n-test_expect_success 'git for-each-ref --stdin: matches' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} --stdin: matches\" '\n \tcat >in <<-EOF &&\n \trefs/tags/multi*\n \trefs/heads/amb*\n@@ -1917,24 +1924,24 @@ test_expect_success 'git for-each-ref --stdin: matches' '\n \trefs/tags/multiline\n \tEOF\n \n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --stdin <in >actual &&\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'git for-each-ref with non-existing refs' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} with non-existing refs\" '\n \tcat >in <<-EOF &&\n \trefs/heads/this-ref-does-not-exist\n \trefs/tags/bogus\n \tEOF\n \n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" --stdin <in >actual &&\n \ttest_must_be_empty actual &&\n \n-\txargs git for-each-ref --format=\"%(refname)\" <in >actual &&\n+\txargs git ${GIT_REFS_LIST_CMD} --format=\"%(refname)\" <in >actual &&\n \ttest_must_be_empty actual\n '\n \n-test_expect_success 'git for-each-ref with nested tags' '\n+test_expect_success \"git ${GIT_REFS_LIST_CMD} with nested tags\" '\n \tgit tag -am \"Normal tag\" nested/base HEAD &&\n \tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n \tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n@@ -1950,14 +1957,14 @@ test_expect_success 'git for-each-ref with nested tags' '\n \trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n \tEOF\n \n-\tgit for-each-ref \\\n+\tgit ${GIT_REFS_LIST_CMD} \\\n \t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n \t\trefs/tags/nested/ >actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'is-base atom with non-commits' '\n-\tgit for-each-ref --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n+\tgit ${GIT_REFS_LIST_CMD} --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n \tgrep \"(HEAD) refs/heads/main\" out &&\n \n \ttest_line_count = 2 err &&\n@@ -2034,7 +2041,7 @@ test_expect_success GPGSSH 'setup for signature atom using ssh' '\n test_expect_success GPG2 'bare signature atom' '\n \tgit verify-commit first-signed 2>expect &&\n \techo  >>expect &&\n-\tgit for-each-ref refs/tags/first-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/first-signed \\\n \t\t--format=\"%(signature)\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2048,7 +2055,7 @@ test_expect_success GPG 'show good signature with custom format' '\n \t73D758744BE721698EC54E8713B6F51ECDDE430D\n \t73D758744BE721698EC54E8713B6F51ECDDE430D\n \tEOF\n-\tgit for-each-ref refs/tags/first-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/first-signed \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2063,7 +2070,7 @@ test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n \n \tEOF\n \tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n-\tgit for-each-ref refs/tags/eighth-signed-ssh \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/eighth-signed-ssh \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2082,7 +2089,7 @@ test_expect_success GPG 'signature atom with grade option and bad signature' '\n \n \n \tEOF\n-\tgit for-each-ref refs/tags/third-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/third-signed \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2095,7 +2102,7 @@ test_expect_success GPG 'show untrusted signature with custom format' '\n \tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n \tD4BE22311AD3131E5EDA29A461092E85B7227189\n \tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/fourth-signed \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2108,7 +2115,7 @@ test_expect_success GPG 'show untrusted signature with undefined trust level' '\n \tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n \tD4BE22311AD3131E5EDA29A461092E85B7227189\n \tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/fourth-signed \\\n \t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2121,7 +2128,7 @@ test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n \t73D758744BE721698EC54E8713B6F51ECDDE430D\n \t73D758744BE721698EC54E8713B6F51ECDDE430D\n \tEOF\n-\tgit for-each-ref refs/tags/sixth-signed \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/sixth-signed \\\n \t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2134,7 +2141,7 @@ test_expect_success GPG 'show unknown signature with custom format' '\n \n \n \tEOF\n-\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git ${GIT_REFS_LIST_CMD} \\\n \t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n@@ -2147,7 +2154,7 @@ test_expect_success GPG 'show lack of signature with custom format' '\n \n \n \tEOF\n-\tgit for-each-ref refs/tags/seventh-unsigned \\\n+\tgit ${GIT_REFS_LIST_CMD} refs/tags/seventh-unsigned \\\n \t\t--format=\"$GRADE_FORMAT\" >actual &&\n \ttest_cmp expect actual\n '\n-- \n2.34.1\n\n"},{"id":"522216","messageId":"CAPig+cQgeqz9bNQSFRvTmoyFgqAwEKSV=KpK18DSP7FA-4GDPw@mail.gmail.com","threadId":"63703","inReplyTo":"20250717075009.26262-2-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH v2 1/2] builtin/refs: add list subcommand","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-07-17T16:48:12Z","receivedAt":"2025-07-17T16:48:24Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jul 17, 2025 at 3:50 AM Meet Soni <meetsoni3017@gmail.com> wrote:\n> Git's reference management is distributed across multiple commands. As\n> part of an ongoing effort to consolidate and modernize reference\n> handling, introduce a `list` subcommand under the `git refs` umbrella as\n> a replacement for `git for-each-ref`.\n>\n> Implement `cmd_refs_list` as a thin wrapper around `cmd_for_each_ref`\n> instead of duplicating its logic. Forward all arguments to the existing\n> function to ensure behavior is identical.\n>\n> Add documentation for the new command. To keep the documentation DRY and\n> consistent with `git-for-each-ref(1)`, refactor the shared command\n> options into a standalone file. Use the AsciiDoc `include::` macro to\n> embed these options in both man pages.\n>\n> This prevents duplication in both code and documentation, ensuring that\n> `refs list` benefits from any future fixes to the underlying\n> `for-each-ref` machinery and its shared documentation.\n>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Mentored-by: shejialuo <shejialuo@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Signed-off-by: Meet Soni <meetsoni3017@gmail.com>\n> ---\n> diff --git a/Documentation/refs-list-options.adoc b/Documentation/refs-list-options.adoc\n> @@ -0,0 +1,80 @@\n> +// Shared options for for-each-ref and refs list\n> +<pattern>...::\n> +       If one or more patterns are given, only refs are shown that\n> +       match against at least one pattern, either using fnmatch(3) or\n> +       literally, in the latter case matching completely or from the\n> +       beginning up to a slash.\n\nNit: We probably don't want the \"// Shared options for...\" comment. If\nsomeone needs to know which commands share this documentation, a\nsuitable \"grep\" invocation will provide the same information but will\nbe correct at that point in time, whereas a comment like this one can\nbecome stale over time.\n"},{"id":"522240","messageId":"xmqqbjpi5y6w.fsf@gitster.g","threadId":"63703","inReplyTo":"20250717075009.26262-3-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH v2 2/2] t: add test for git refs list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-17T21:01:27Z","receivedAt":"2025-07-17T21:01:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Meet Soni <meetsoni3017@gmail.com> writes:\n\n> +. ./test-lib.sh\n> +\n> +GIT_REFS_LIST_CMD='refs list'\n> +. \"$TEST_DIRECTORY\"/t6300-for-each-ref.sh\n> ...\n> -\tgit for-each-ref refs/tags/fourth-signed \\\n> +\tgit ${GIT_REFS_LIST_CMD} refs/tags/fourth-signed \\\n\nI know where your bias comes from ;-) but if this were\n\n> -\tgit for-each-ref refs/tags/fourth-signed \\\n> +\t$git_for_each_ref refs/tags/fourth-signed \\\n\nit would have been easier to read the resulting test, as\nt6300-for-each-ref is and has been primarily about \"git\nfor-each-ref\" command, and the new test script that overrides\n\n    git_for_each_ref=\"git refs list\"\n\nbefore including t6300 would be a good demonstration that the new\n\"git ref list\" command can stand in as its replacement.\n\n> +GIT_REFS_LIST_CMD='refs list'\n> +. \"$TEST_DIRECTORY\"/t6300-for-each-ref.sh\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index ce9af79ab1..74a030371c 100755\n> --- a/t/t6300-for-each-ref.sh\n> +++ b/t/t6300-for-each-ref.sh\n> @@ -5,7 +5,9 @@\n>  \n>  test_description='for-each-ref test'\n>  \n> -. ./test-lib.sh\n> +. \"${TEST_DIRECTORY:-.}/test-lib.sh\"\n\nThis is probably wrong.\n\nNobody promises that including test-lib.sh is and will forever be\nidempotent.  While this patch may not give t6300 a serious\nregression right now, I am not sure what future subtle breakage we\nare looking at for t1461.\n\nProbably this should model itself after how 8752d11d (git-blame: Use\nthe same tests for git-blame as for git-annotate, 2006-03-05) moved\na lot from existing t8001 to annotate-tests and so that they can be\nshared with new t8002.\n\nThanks.\n"},{"id":"522519","messageId":"CAPhwyn0a6g1HCSKqN5oUxRCp9qfThqBDady1M9kDJikP1fc+3w@mail.gmail.com","threadId":"63703","inReplyTo":"CAPig+cQgeqz9bNQSFRvTmoyFgqAwEKSV=KpK18DSP7FA-4GDPw@mail.gmail.com","subject":"Re: [GSoC][RFC PATCH v2 1/2] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-23T05:01:45Z","receivedAt":"2025-07-23T05:01:57Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"On Thu, 17 Jul 2025 at 22:18, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Thu, Jul 17, 2025 at 3:50 AM Meet Soni <meetsoni3017@gmail.com> wrote:\n[snip]\n\n> > diff --git a/Documentation/refs-list-options.adoc b/Documentation/refs-list-options.adoc\n> > @@ -0,0 +1,80 @@\n> > +// Shared options for for-each-ref and refs list\n> > +<pattern>...::\n> > +       If one or more patterns are given, only refs are shown that\n> > +       match against at least one pattern, either using fnmatch(3) or\n> > +       literally, in the latter case matching completely or from the\n> > +       beginning up to a slash.\n>\n> Nit: We probably don't want the \"// Shared options for...\" comment. If\n> someone needs to know which commands share this documentation, a\n> suitable \"grep\" invocation will provide the same information but will\n> be correct at that point in time, whereas a comment like this one can\n> become stale over time.\n\n\nMakes sense, I'll remove it in the next version.\n\nApologies for the delayed response, I was away last week.\nThanks\n"},{"id":"522520","messageId":"CAPhwyn0wLrTy0MnNYdQvoVPXUz0PyXDB0MBsDHnDR_zgA8mZmg@mail.gmail.com","threadId":"63703","inReplyTo":"xmqqbjpi5y6w.fsf@gitster.g","subject":"Re: [GSoC][RFC PATCH v2 2/2] t: add test for git refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-23T05:17:41Z","receivedAt":"2025-07-23T05:17:53Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"On Fri, 18 Jul 2025 at 02:31, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Meet Soni <meetsoni3017@gmail.com> writes:\n>\n> > +. ./test-lib.sh\n> > +\n> > +GIT_REFS_LIST_CMD='refs list'\n> > +. \"$TEST_DIRECTORY\"/t6300-for-each-ref.sh\n> > ...\n> > -     git for-each-ref refs/tags/fourth-signed \\\n> > +     git ${GIT_REFS_LIST_CMD} refs/tags/fourth-signed \\\n>\n> I know where your bias comes from ;-) but if this were\n>\n> > -     git for-each-ref refs/tags/fourth-signed \\\n> > +     $git_for_each_ref refs/tags/fourth-signed \\\n>\n> it would have been easier to read the resulting test, as\n> t6300-for-each-ref is and has been primarily about \"git\n> for-each-ref\" command, and the new test script that overrides\n>\n>     git_for_each_ref=\"git refs list\"\n>\n> before including t6300 would be a good demonstration that the new\n> \"git ref list\" command can stand in as its replacement.\n>\n> > +GIT_REFS_LIST_CMD='refs list'\n> > +. \"$TEST_DIRECTORY\"/t6300-for-each-ref.sh\n> > diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> > index ce9af79ab1..74a030371c 100755\n> > --- a/t/t6300-for-each-ref.sh\n> > +++ b/t/t6300-for-each-ref.sh\n> > @@ -5,7 +5,9 @@\n> >\n> >  test_description='for-each-ref test'\n> >\n> > -. ./test-lib.sh\n> > +. \"${TEST_DIRECTORY:-.}/test-lib.sh\"\n>\n> This is probably wrong.\n>\n> Nobody promises that including test-lib.sh is and will forever be\n> idempotent.  While this patch may not give t6300 a serious\n> regression right now, I am not sure what future subtle breakage we\n> are looking at for t1461.\n>\n> Probably this should model itself after how 8752d11d (git-blame: Use\n> the same tests for git-blame as for git-annotate, 2006-03-05) moved\n> a lot from existing t8001 to annotate-tests and so that they can be\n> shared with new t8002.\n>\n> Thanks.\n\nApologies for the delayed response, I was away last week.\n\nThanks for the review. The suggestions for the test suite make sense.\nI'll refactor it accordingly and send out a v3.\n\nThanks.\n"},{"id":"522525","messageId":"20250723064313.29866-1-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250717075009.26262-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v3 0/3] Add refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-23T06:43:10Z","receivedAt":"2025-07-23T06:43:21Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Hello everyone,\n\nThis is the third version of the patch series that introduces the `git\nrefs list` subcommand as a modern alternative to `git for-each-ref`.\n\nThank you to everyone who provided valuable feedback on the patch\nseries.\n\nChanges in v3:\n  - Restructured the patch series from two commits into three, providing\n    a cleaner separation between the feature implementation, the test\n    refactoring, and the new test itself.\n\n  - Reworked the testing approach to follow the established pattern for\n    shared tests (like git-blame/annotate). The core test logic now\n    resides in a shared file (`for-each-ref-tests.sh`), which is sourced\n    by both the original `t6300-for-each-ref.sh` and the new\n    `t1461-refs-list.sh` test drivers.\n\n  - Adopted the suggested `$git_for_each_ref` variable convention in the\n    test suite for better clarity and to demonstrate the drop-in\n    compatibility.\n\n  - Removed unwanted comment.\n\n---\n\n(v1 cover-letter text)\n\nThis patch series introduces `git refs list` as a modern replacement for\n`git for-each-ref`, as part of an effort to consolidate ref-related\nfunctionality under a unified `git refs` command.\n\nGit's ref-related operations are currently handled by several distinct\ncommands, such as `git show-ref`, `git for-each-ref`, `git update-ref`,\n`git pack-refs`, etc. This distribution has a few practical drawbacks:\n\n- Users need to rely on multiple commands for related tasks involving\n  refs.\n\n- The commands may differ slightly in behavior and option syntax,\n  leading to inconsistency.\n\nWe propose a long-term consolidation effort to bring ref-related\nsubcommands under the umbrella of a single command: `git refs`.\n\nThe implementation of `git refs list` is functionally identical to `git\nfor-each-ref`. It reuses the same internal logic (cmd_for_each_ref) to\nensure complete backward compatibility. The purpose of this patch is not\nto introduce new behavior but to provide an alternate entry point under\nthe consolidated `git refs` namespace.\n\nThe motivation behind this change is twofold:\n\n- Consolidation: Centralizing ref-related operations makes them easier\n  to discover, use, and maintain.\n\n- Evolution: While the initial goal is parity with existing commands,\n  this consolidation allows for careful reconsideration of which\n  features are essential. Over time, we can:\n\n  - Remove legacy or obscure options that are no longer needed.\n  - Add improvements that wouldn't make sense to bolt onto legacy\n    commands.\n  - Offering a more consistent and user-friendly surface.\n\nTo verify backward compatibility, this patch also includes a test\n`t/t1461-refs-list.sh`, which runs the full `t6300-for-each-ref.sh` test\nusing `git refs list`. The test uses ${GIT_REFS_LIST_CMD:-for-each-ref}\nto allow substitution without duplicating tests.\n\nThis patch is deliberately conservative: it introduces no behavioral\nchanges and leaves `for-each-ref` untouched. The goal is to lay\ngroundwork and demonstrate viability of ref consolidation within `git\nrefs`.\n\nGoing forward, I'd like to initiate a discussion on what the ideal\nsurface of `git refs list` should look like. Which options and features\nfrom `for-each-ref` should be carried over? Are there any that are\nobsolete or overly niche? What improvements might be worth considering\nnow that we have a new, consolidated interface?\n\nFeedback on this, especially from those who rely on `for-each-ref` in\nscripts or tooling would be very helpful.\n\nMeet Soni (3):\n  builtin/refs: add list subcommand\n  t6300: refactor tests to be shareable\n  t: add test for git refs list subcommand\n\n Documentation/git-for-each-ref.adoc  |   80 +-\n Documentation/git-refs.adoc          |   16 +\n Documentation/refs-list-options.adoc |   79 +\n builtin/for-each-ref.c               |   24 +-\n builtin/refs.c                       |   35 +\n t/for-each-ref-tests.sh              | 2141 ++++++++++++++++++++++++++\n t/meson.build                        |    1 +\n t/t1461-refs-list.sh                 |    8 +\n t/t6300-for-each-ref.sh              | 2140 +------------------------\n 9 files changed, 2303 insertions(+), 2221 deletions(-)\n create mode 100644 Documentation/refs-list-options.adoc\n create mode 100644 t/for-each-ref-tests.sh\n create mode 100755 t/t1461-refs-list.sh\n\nRange-diff against v2:\n1:  b2d3026520 ! 1:  547b0bbf8f builtin/refs: add list subcommand\n    @@ Documentation/git-refs.adoc: The following options are specific to 'git refs ver\n     \n      ## Documentation/refs-list-options.adoc (new) ##\n     @@\n    -+// Shared options for for-each-ref and refs list\n     +<pattern>...::\n     +\tIf one or more patterns are given, only refs are shown that\n     +\tmatch against at least one pattern, either using fnmatch(3) or\n2:  2d6534841f < -:  ---------- t: add test for git refs list subcommand\n-:  ---------- > 2:  5d5057ff98 t6300: refactor tests to be shareable\n-:  ---------- > 3:  b9cb9cdf48 t: add test for git refs list subcommand\n-- \n2.34.1\n\n"},{"id":"522526","messageId":"20250723064313.29866-2-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250723064313.29866-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v3 1/3] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-23T06:43:11Z","receivedAt":"2025-07-23T06:43:25Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Git's reference management is distributed across multiple commands. As\npart of an ongoing effort to consolidate and modernize reference\nhandling, introduce a `list` subcommand under the `git refs` umbrella as\na replacement for `git for-each-ref`.\n\nImplement `cmd_refs_list` as a thin wrapper around `cmd_for_each_ref`\ninstead of duplicating its logic. Forward all arguments to the existing\nfunction to ensure behavior is identical.\n\nAdd documentation for the new command. To keep the documentation DRY and\nconsistent with `git-for-each-ref(1)`, refactor the shared command\noptions into a standalone file. Use the AsciiDoc `include::` macro to\nembed these options in both man pages.\n\nThis prevents duplication in both code and documentation, ensuring that\n`refs list` benefits from any future fixes to the underlying\n`for-each-ref` machinery and its shared documentation.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/git-for-each-ref.adoc  | 80 +---------------------------\n Documentation/git-refs.adoc          | 16 ++++++\n Documentation/refs-list-options.adoc | 79 +++++++++++++++++++++++++++\n builtin/for-each-ref.c               | 24 +++++++--\n builtin/refs.c                       | 35 ++++++++++++\n 5 files changed, 151 insertions(+), 83 deletions(-)\n create mode 100644 Documentation/refs-list-options.adoc\n\ndiff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\nindex 5ef89fc0fe..f7bbc1902a 100644\n--- a/Documentation/git-for-each-ref.adoc\n+++ b/Documentation/git-for-each-ref.adoc\n@@ -28,85 +28,7 @@ host language allowing their direct evaluation in that language.\n \n OPTIONS\n -------\n-<pattern>...::\n-\tIf one or more patterns are given, only refs are shown that\n-\tmatch against at least one pattern, either using fnmatch(3) or\n-\tliterally, in the latter case matching completely or from the\n-\tbeginning up to a slash.\n-\n---stdin::\n-\tIf `--stdin` is supplied, then the list of patterns is read from\n-\tstandard input instead of from the argument list.\n-\n---count=<count>::\n-\tBy default the command shows all refs that match\n-\t`<pattern>`.  This option makes it stop after showing\n-\tthat many refs.\n-\n---sort=<key>::\n-\tA field name to sort on.  Prefix `-` to sort in\n-\tdescending order of the value.  When unspecified,\n-\t`refname` is used.  You may use the --sort=<key> option\n-\tmultiple times, in which case the last key becomes the primary\n-\tkey.\n-\n---format=<format>::\n-\tA string that interpolates `%(fieldname)` from a ref being shown and\n-\tthe object it points at. In addition, the string literal `%%`\n-\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n-\tthe character with hex code `xx`. For example, `%00` interpolates to\n-\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n-+\n-When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n-TAB %(refname)`.\n-\n---color[=<when>]::\n-\tRespect any colors specified in the `--format` option. The\n-\t`<when>` field must be one of `always`, `never`, or `auto` (if\n-\t`<when>` is absent, behave as if `always` was given).\n-\n---shell::\n---perl::\n---python::\n---tcl::\n-\tIf given, strings that substitute `%(fieldname)`\n-\tplaceholders are quoted as string literals suitable for\n-\tthe specified host language.  This is meant to produce\n-\ta scriptlet that can directly be `eval`ed.\n-\n---points-at=<object>::\n-\tOnly list refs which points at the given object.\n-\n---merged[=<object>]::\n-\tOnly list refs whose tips are reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---no-merged[=<object>]::\n-\tOnly list refs whose tips are not reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---contains[=<object>]::\n-\tOnly list refs which contain the specified commit (HEAD if not\n-\tspecified).\n-\n---no-contains[=<object>]::\n-\tOnly list refs which don't contain the specified commit (HEAD\n-\tif not specified).\n-\n---ignore-case::\n-\tSorting and filtering refs are case insensitive.\n-\n---omit-empty::\n-\tDo not print a newline after formatted refs where the format expands\n-\tto the empty string.\n-\n---exclude=<pattern>::\n-\tIf one or more patterns are given, only refs which do not match\n-\tany excluded pattern(s) are shown. Matching is done using the\n-\tsame rules as `<pattern>` above.\n-\n---include-root-refs::\n-\tList root refs (HEAD and pseudorefs) apart from regular refs.\n+include::refs-list-options.adoc[]\n \n FIELD NAMES\n -----------\ndiff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\nindex 4d6dc994f9..ded90f435b 100644\n--- a/Documentation/git-refs.adoc\n+++ b/Documentation/git-refs.adoc\n@@ -11,6 +11,13 @@ SYNOPSIS\n [synopsis]\n git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n git refs verify [--strict] [--verbose]\n+git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n+\t      [(--sort=<key>)...] [--format=<format>]\n+\t      [--include-root-refs] [ --stdin | <pattern>... ]\n+\t      [--points-at=<object>]\n+\t      [--merged[=<object>]] [--no-merged[=<object>]]\n+\t      [--contains[=<object>]] [--no-contains[=<object>]]\n+\t      [--exclude=<pattern> ...]\n \n DESCRIPTION\n -----------\n@@ -26,6 +33,11 @@ migrate::\n verify::\n \tVerify reference database consistency.\n \n+list::\n+\tList references in the repository with support for filtering,\n+\tformatting, and sorting. This subcommand is an alias for\n+\tlinkgit:git-for-each-ref[1] and offers identical functionality.\n+\n OPTIONS\n -------\n \n@@ -57,6 +69,10 @@ The following options are specific to 'git refs verify':\n --verbose::\n \tWhen verifying the reference database consistency, be chatty.\n \n+The following options are specific to 'git refs list':\n+\n+include::refs-list-options.adoc[]\n+\n KNOWN LIMITATIONS\n -----------------\n \ndiff --git a/Documentation/refs-list-options.adoc b/Documentation/refs-list-options.adoc\nnew file mode 100644\nindex 0000000000..5f3a85bf64\n--- /dev/null\n+++ b/Documentation/refs-list-options.adoc\n@@ -0,0 +1,79 @@\n+<pattern>...::\n+\tIf one or more patterns are given, only refs are shown that\n+\tmatch against at least one pattern, either using fnmatch(3) or\n+\tliterally, in the latter case matching completely or from the\n+\tbeginning up to a slash.\n+\n+--stdin::\n+\tIf `--stdin` is supplied, then the list of patterns is read from\n+\tstandard input instead of from the argument list.\n+\n+--count=<count>::\n+\tBy default the command shows all refs that match\n+\t`<pattern>`.  This option makes it stop after showing\n+\tthat many refs.\n+\n+--sort=<key>::\n+\tA field name to sort on.  Prefix `-` to sort in\n+\tdescending order of the value.  When unspecified,\n+\t`refname` is used.  You may use the --sort=<key> option\n+\tmultiple times, in which case the last key becomes the primary\n+\tkey.\n+\n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from a ref being shown and\n+\tthe object it points at. In addition, the string literal `%%`\n+\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n+\tthe character with hex code `xx`. For example, `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n++\n+When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n+TAB %(refname)`.\n+\n+--color[=<when>]::\n+\tRespect any colors specified in the `--format` option. The\n+\t`<when>` field must be one of `always`, `never`, or `auto` (if\n+\t`<when>` is absent, behave as if `always` was given).\n+\n+--shell::\n+--perl::\n+--python::\n+--tcl::\n+\tIf given, strings that substitute `%(fieldname)`\n+\tplaceholders are quoted as string literals suitable for\n+\tthe specified host language.  This is meant to produce\n+\ta scriptlet that can directly be `eval`ed.\n+\n+--points-at=<object>::\n+\tOnly list refs which points at the given object.\n+\n+--merged[=<object>]::\n+\tOnly list refs whose tips are reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--no-merged[=<object>]::\n+\tOnly list refs whose tips are not reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--contains[=<object>]::\n+\tOnly list refs which contain the specified commit (HEAD if not\n+\tspecified).\n+\n+--no-contains[=<object>]::\n+\tOnly list refs which don't contain the specified commit (HEAD\n+\tif not specified).\n+\n+--ignore-case::\n+\tSorting and filtering refs are case insensitive.\n+\n+--omit-empty::\n+\tDo not print a newline after formatted refs where the format expands\n+\tto the empty string.\n+\n+--exclude=<pattern>::\n+\tIf one or more patterns are given, only refs which do not match\n+\tany excluded pattern(s) are shown. Matching is done using the\n+\tsame rules as `<pattern>` above.\n+\n+--include-root-refs::\n+\tList root refs (HEAD and pseudorefs) apart from regular refs.\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 3d2207ec77..d7d8279049 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -16,11 +16,27 @@ static char const * const for_each_ref_usage[] = {\n \tNULL\n };\n \n+#define REFS_LIST_USAGE \\\n+\tN_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t   \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n+\t   \"              [--points-at=<object>]\\n\" \\\n+\t   \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t   \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t   \"              [--exclude=<pattern> ...]\")\n+\n+static char const * const refs_list_usage[] = {\n+\tREFS_LIST_USAGE,\n+\tNULL\n+};\n+\n int cmd_for_each_ref(int argc,\n \t\t     const char **argv,\n \t\t     const char *prefix,\n \t\t     struct repository *repo)\n {\n+\tint cmd_is_refs_list = !strcmp(argv[0], \"refs list\");\n+\tconst char *const *opt_usage = cmd_is_refs_list ? refs_list_usage : for_each_ref_usage;\n \tstruct ref_sorting *sorting;\n \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n \tint icase = 0, include_root_refs = 0, from_stdin = 0;\n@@ -67,17 +83,17 @@ int cmd_for_each_ref(int argc,\n \t/* Set default (refname) sorting */\n \tstring_list_append(&sorting_options, \"refname\");\n \n-\tparse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);\n+\tparse_options(argc, argv, prefix, opts, opt_usage, 0);\n \tif (format.array_opts.max_count < 0) {\n \t\terror(\"invalid --count argument: `%d'\", format.array_opts.max_count);\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(opt_usage, opts);\n \t}\n \tif (HAS_MULTI_BITS(format.quote_style)) {\n \t\terror(\"more than one quoting style?\");\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(opt_usage, opts);\n \t}\n \tif (verify_ref_format(&format))\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(opt_usage, opts);\n \n \tsorting = ref_sorting_options(&sorting_options);\n \tref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);\ndiff --git a/builtin/refs.c b/builtin/refs.c\nindex 998d2a2c1c..41e29d1b5f 100644\n--- a/builtin/refs.c\n+++ b/builtin/refs.c\n@@ -3,6 +3,7 @@\n #include \"config.h\"\n #include \"fsck.h\"\n #include \"parse-options.h\"\n+#include \"ref-filter.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n #include \"worktree.h\"\n@@ -13,6 +14,15 @@\n #define REFS_VERIFY_USAGE \\\n \tN_(\"git refs verify [--strict] [--verbose]\")\n \n+#define REFS_LIST_USAGE \\\n+\tN_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t   \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n+\t   \"              [--points-at=<object>]\\n\" \\\n+\t   \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t   \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t   \"              [--exclude=<pattern> ...]\")\n+\n static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,\n \t\t\t    struct repository *repo UNUSED)\n {\n@@ -101,6 +111,29 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,\n \treturn ret;\n }\n \n+static int cmd_refs_list(int argc, const char **argv, const char *prefix,\n+\t\t  struct repository *repo)\n+{\n+\tstruct strvec args = STRVEC_INIT;\n+\tconst char **args_copy;\n+\tint ret;\n+\n+\tstrvec_push(&args, \"refs list\");\n+\n+\tfor (int i = 1; i < argc; i++)\n+\t\tstrvec_push(&args, argv[i]);\n+\n+\tCALLOC_ARRAY(args_copy, args.nr + 1);\n+\tCOPY_ARRAY(args_copy, args.v, args.nr);\n+\n+\tret = cmd_for_each_ref(args.nr, args_copy, prefix, repo);\n+\n+\tstrvec_clear(&args);\n+\tfree(args_copy);\n+\n+\treturn ret;\n+}\n+\n int cmd_refs(int argc,\n \t     const char **argv,\n \t     const char *prefix,\n@@ -109,12 +142,14 @@ int cmd_refs(int argc,\n \tconst char * const refs_usage[] = {\n \t\tREFS_MIGRATE_USAGE,\n \t\tREFS_VERIFY_USAGE,\n+\t\tREFS_LIST_USAGE,\n \t\tNULL,\n \t};\n \tparse_opt_subcommand_fn *fn = NULL;\n \tstruct option opts[] = {\n \t\tOPT_SUBCOMMAND(\"migrate\", &fn, cmd_refs_migrate),\n \t\tOPT_SUBCOMMAND(\"verify\", &fn, cmd_refs_verify),\n+\t\tOPT_SUBCOMMAND(\"list\", &fn, cmd_refs_list),\n \t\tOPT_END(),\n \t};\n \n-- \n2.34.1\n\n"},{"id":"522528","messageId":"20250723064313.29866-3-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250723064313.29866-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v3 2/3] t6300: refactor tests to be shareable","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-23T06:43:12Z","receivedAt":"2025-07-23T06:43:32Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"In preparation for adding tests for the new `git refs list` command,\nrefactor the existing t6300 test suite to make its logic shareable.\n\nMove the core test logic from `t6300-for-each-ref.sh` into a new\n`for-each-ref-tests.sh` file. Inside this new script, replace hardcoded\ncalls to \"git for-each-ref\" with the `$git_for_each_ref` variable.\n\nThe original `t6300-for-each-ref.sh` script now becomes a simple\n\"driver\". It is responsible for setting the default value of the\nvariable and then sourcing the test library.\n\nThis new structure follows the established pattern used for sharing\ntests between `git-blame` and `git-annotate` and prepares the test suite\nfor the `refs list` tests to be added in a subsequent commit.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/for-each-ref-tests.sh | 2141 +++++++++++++++++++++++++++++++++++++++\n t/t6300-for-each-ref.sh | 2140 +-------------------------------------\n 2 files changed, 2143 insertions(+), 2138 deletions(-)\n create mode 100644 t/for-each-ref-tests.sh\n\ndiff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh\nnew file mode 100644\nindex 0000000000..e3ad19298a\n--- /dev/null\n+++ b/t/for-each-ref-tests.sh\n@@ -0,0 +1,2141 @@\n+git_for_each_ref=${git_for_each_ref:-git for-each-ref}\n+GNUPGHOME_NOT_USED=$GNUPGHOME\n+. \"$TEST_DIRECTORY\"/lib-gpg.sh\n+. \"$TEST_DIRECTORY\"/lib-terminal.sh\n+\n+# Mon Jul 3 23:18:43 2006 +0000\n+datestamp=1151968723\n+setdate_and_increment () {\n+    GIT_COMMITTER_DATE=\"$datestamp +0200\"\n+    datestamp=$(expr \"$datestamp\" + 1)\n+    GIT_AUTHOR_DATE=\"$datestamp +0200\"\n+    datestamp=$(expr \"$datestamp\" + 1)\n+    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n+}\n+\n+test_object_file_size () {\n+\toid=$(git rev-parse \"$1\")\n+\tpath=\".git/objects/$(test_oid_to_path $oid)\"\n+\ttest_file_size \"$path\"\n+}\n+\n+test_expect_success setup '\n+\t# setup .mailmap\n+\tcat >.mailmap <<-EOF &&\n+\tA Thor <athor@example.com> A U Thor <author@example.com>\n+\tC Mitter <cmitter@example.com> C O Mitter <committer@example.com>\n+\tEOF\n+\n+\tsetdate_and_increment &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Initial\" &&\n+\tgit branch -M main &&\n+\tsetdate_and_increment &&\n+\tgit tag -a -m \"Tagging at $datestamp\" testtag &&\n+\tgit update-ref refs/remotes/origin/main main &&\n+\tgit remote add origin nowhere &&\n+\tgit config branch.main.remote origin &&\n+\tgit config branch.main.merge refs/heads/main &&\n+\tgit remote add myfork elsewhere &&\n+\tgit config remote.pushdefault myfork &&\n+\tgit config push.default current\n+'\n+\n+test_atom () {\n+\tcase \"$1\" in\n+\t\thead) ref=refs/heads/main ;;\n+\t\t tag) ref=refs/tags/testtag ;;\n+\t\t sym) ref=refs/heads/sym ;;\n+\t\t   *) ref=$1 ;;\n+\tesac\n+\tformat=$2\n+\ttest_do=test_expect_${4:-success}\n+\n+\tprintf '%s\\n' \"$3\" >expected\n+\t$test_do $PREREQ \"basic atom: $ref $format\" '\n+\t\t${git_for_each_ref} --format=\"%($format)\" \"$ref\" >actual &&\n+\t\tsanitize_pgp <actual >actual.clean &&\n+\t\ttest_cmp expected actual.clean\n+\t'\n+\n+\t# Automatically test \"contents:size\" atom after testing \"contents\"\n+\tif test \"$format\" = \"contents\"\n+\tthen\n+\t\t# for commit leg, $3 is changed there\n+\t\texpect=$(printf '%s' \"$3\" | wc -c)\n+\t\t$test_do $PREREQ \"basic atom: $ref contents:size\" '\n+\t\t\ttype=$(git cat-file -t \"$ref\") &&\n+\t\t\tcase $type in\n+\t\t\ttag)\n+\t\t\t\t# We cannot use $3 as it expects sanitize_pgp to run\n+\t\t\t\tgit cat-file tag $ref >out &&\n+\t\t\t\texpect=$(tail -n +6 out | wc -c) &&\n+\t\t\t\trm -f out ;;\n+\t\t\ttree | blob)\n+\t\t\t\texpect=\"\" ;;\n+\t\t\tcommit)\n+\t\t\t\t: \"use the calculated expect\" ;;\n+\t\t\t*)\n+\t\t\t\tBUG \"unknown object type\" ;;\n+\t\t\tesac &&\n+\t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n+\t\t\techo $expect >expected &&\n+\t\t\t${git_for_each_ref} --format=\"%(contents:size)\" \"$ref\" >actual &&\n+\t\t\ttest_cmp expected actual\n+\t\t'\n+\tfi\n+}\n+\n+hexlen=$(test_oid hexsz)\n+\n+test_atom head refname refs/heads/main\n+test_atom head refname: refs/heads/main\n+test_atom head refname:short main\n+test_atom head refname:lstrip=1 heads/main\n+test_atom head refname:lstrip=2 main\n+test_atom head refname:lstrip=-1 main\n+test_atom head refname:lstrip=-2 heads/main\n+test_atom head refname:rstrip=1 refs/heads\n+test_atom head refname:rstrip=2 refs\n+test_atom head refname:rstrip=-1 refs\n+test_atom head refname:rstrip=-2 refs/heads\n+test_atom head refname:strip=1 heads/main\n+test_atom head refname:strip=2 main\n+test_atom head refname:strip=-1 main\n+test_atom head refname:strip=-2 heads/main\n+test_atom head upstream refs/remotes/origin/main\n+test_atom head upstream:short origin/main\n+test_atom head upstream:lstrip=2 origin/main\n+test_atom head upstream:lstrip=-2 origin/main\n+test_atom head upstream:rstrip=2 refs/remotes\n+test_atom head upstream:rstrip=-2 refs/remotes\n+test_atom head upstream:strip=2 origin/main\n+test_atom head upstream:strip=-2 origin/main\n+test_atom head push refs/remotes/myfork/main\n+test_atom head push:short myfork/main\n+test_atom head push:lstrip=1 remotes/myfork/main\n+test_atom head push:lstrip=-1 main\n+test_atom head push:rstrip=1 refs/remotes/myfork\n+test_atom head push:rstrip=-1 refs\n+test_atom head push:strip=1 remotes/myfork/main\n+test_atom head push:strip=-1 main\n+test_atom head objecttype commit\n+test_atom head objectsize $((131 + hexlen))\n+test_atom head objectsize:disk $(test_object_file_size refs/heads/main)\n+test_atom head deltabase $ZERO_OID\n+test_atom head objectname $(git rev-parse refs/heads/main)\n+test_atom head objectname:short $(git rev-parse --short refs/heads/main)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n+test_atom head tree $(git rev-parse refs/heads/main^{tree})\n+test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})\n+test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})\n+test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})\n+test_atom head parent ''\n+test_atom head parent:short ''\n+test_atom head parent:short=1 ''\n+test_atom head parent:short=10 ''\n+test_atom head numparent 0\n+test_atom head object ''\n+test_atom head type ''\n+test_atom head raw \"$(git cat-file commit refs/heads/main)\n+\"\n+test_atom head '*objectname' ''\n+test_atom head '*objecttype' ''\n+test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n+test_atom head authorname 'A U Thor'\n+test_atom head authorname:mailmap 'A Thor'\n+test_atom head authoremail '<author@example.com>'\n+test_atom head authoremail:trim 'author@example.com'\n+test_atom head authoremail:localpart 'author'\n+test_atom head authoremail:trim,localpart 'author'\n+test_atom head authoremail:mailmap '<athor@example.com>'\n+test_atom head authoremail:mailmap,trim 'athor@example.com'\n+test_atom head authoremail:trim,mailmap 'athor@example.com'\n+test_atom head authoremail:mailmap,localpart 'athor'\n+test_atom head authoremail:localpart,mailmap 'athor'\n+test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'\n+test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n+test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n+test_atom head committername 'C O Mitter'\n+test_atom head committername:mailmap 'C Mitter'\n+test_atom head committeremail '<committer@example.com>'\n+test_atom head committeremail:trim 'committer@example.com'\n+test_atom head committeremail:localpart 'committer'\n+test_atom head committeremail:localpart,trim 'committer'\n+test_atom head committeremail:mailmap '<cmitter@example.com>'\n+test_atom head committeremail:mailmap,trim 'cmitter@example.com'\n+test_atom head committeremail:trim,mailmap 'cmitter@example.com'\n+test_atom head committeremail:mailmap,localpart 'cmitter'\n+test_atom head committeremail:localpart,mailmap 'cmitter'\n+test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'\n+test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n+test_atom head tag ''\n+test_atom head tagger ''\n+test_atom head taggername ''\n+test_atom head taggeremail ''\n+test_atom head taggeremail:trim ''\n+test_atom head taggeremail:localpart ''\n+test_atom head taggerdate ''\n+test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n+test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n+test_atom head subject 'Initial'\n+test_atom head subject:sanitize 'Initial'\n+test_atom head contents:subject 'Initial'\n+test_atom head body ''\n+test_atom head contents:body ''\n+test_atom head contents:signature ''\n+test_atom head contents 'Initial\n+'\n+test_atom head HEAD '*'\n+\n+test_atom tag refname refs/tags/testtag\n+test_atom tag refname:short testtag\n+test_atom tag upstream ''\n+test_atom tag push ''\n+test_atom tag objecttype tag\n+test_atom tag objectsize $((114 + hexlen))\n+test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)\n+test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)\n+test_atom tag deltabase $ZERO_OID\n+test_atom tag '*deltabase' $ZERO_OID\n+test_atom tag objectname $(git rev-parse refs/tags/testtag)\n+test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n+test_atom tag tree ''\n+test_atom tag tree:short ''\n+test_atom tag tree:short=1 ''\n+test_atom tag tree:short=10 ''\n+test_atom tag parent ''\n+test_atom tag parent:short ''\n+test_atom tag parent:short=1 ''\n+test_atom tag parent:short=10 ''\n+test_atom tag numparent ''\n+test_atom tag object $(git rev-parse refs/tags/testtag^0)\n+test_atom tag type 'commit'\n+test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})\n+test_atom tag '*objecttype' 'commit'\n+test_atom tag author ''\n+test_atom tag authorname ''\n+test_atom tag authorname:mailmap ''\n+test_atom tag authoremail ''\n+test_atom tag authoremail:trim ''\n+test_atom tag authoremail:localpart ''\n+test_atom tag authoremail:trim,localpart ''\n+test_atom tag authoremail:mailmap ''\n+test_atom tag authoremail:mailmap,trim ''\n+test_atom tag authoremail:trim,mailmap ''\n+test_atom tag authoremail:mailmap,localpart ''\n+test_atom tag authoremail:localpart,mailmap ''\n+test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''\n+test_atom tag authordate ''\n+test_atom tag committer ''\n+test_atom tag committername ''\n+test_atom tag committername:mailmap ''\n+test_atom tag committeremail ''\n+test_atom tag committeremail:trim ''\n+test_atom tag committeremail:localpart ''\n+test_atom tag committeremail:localpart,trim ''\n+test_atom tag committeremail:mailmap ''\n+test_atom tag committeremail:mailmap,trim ''\n+test_atom tag committeremail:trim,mailmap ''\n+test_atom tag committeremail:mailmap,localpart ''\n+test_atom tag committeremail:localpart,mailmap ''\n+test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''\n+test_atom tag committerdate ''\n+test_atom tag tag 'testtag'\n+test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n+test_atom tag taggername 'C O Mitter'\n+test_atom tag taggername:mailmap 'C Mitter'\n+test_atom tag taggeremail '<committer@example.com>'\n+test_atom tag taggeremail:trim 'committer@example.com'\n+test_atom tag taggeremail:localpart 'committer'\n+test_atom tag taggeremail:trim,localpart 'committer'\n+test_atom tag taggeremail:mailmap '<cmitter@example.com>'\n+test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'\n+test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'\n+test_atom tag taggeremail:mailmap,localpart 'cmitter'\n+test_atom tag taggeremail:localpart,mailmap 'cmitter'\n+test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'\n+test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n+test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n+test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n+test_atom tag subject 'Tagging at 1151968727'\n+test_atom tag subject:sanitize 'Tagging-at-1151968727'\n+test_atom tag contents:subject 'Tagging at 1151968727'\n+test_atom tag body ''\n+test_atom tag contents:body ''\n+test_atom tag contents:signature ''\n+test_atom tag contents 'Tagging at 1151968727\n+'\n+test_atom tag HEAD ' '\n+\n+test_expect_success 'basic atom: refs/tags/testtag *raw' '\n+\tgit cat-file commit refs/tags/testtag^{} >expected &&\n+\t${git_for_each_ref} --format=\"%(*raw)\" refs/tags/testtag >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_expect_success 'Check invalid atoms names are errors' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(INVALID)\" refs/heads\n+'\n+\n+test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n+\t${git_for_each_ref} --format=\"%(authordate)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n+'\n+\n+test_expect_success 'Check valid format specifiers for date fields' '\n+\t${git_for_each_ref} --format=\"%(authordate:default)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:relative)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:short)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:local)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:iso8601)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:rfc2822)\" refs/heads\n+'\n+\n+test_expect_success 'Check invalid format specifiers are errors' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(authordate:INVALID)\" refs/heads\n+'\n+\n+test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=0)\" &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=-1)\" &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=foo)\"\n+'\n+\n+test_bad_atom () {\n+\tcase \"$1\" in\n+\thead) ref=refs/heads/main ;;\n+\t tag) ref=refs/tags/testtag ;;\n+\t sym) ref=refs/heads/sym ;;\n+\t   *) ref=$1 ;;\n+\tesac\n+\tformat=$2\n+\ttest_do=test_expect_${4:-success}\n+\n+\tprintf '%s\\n' \"$3\" >expect\n+\t$test_do $PREREQ \"err basic atom: $ref $format\" '\n+\t\ttest_must_fail ${git_for_each_ref} \\\n+\t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n+\t\ttest_cmp expect error\n+\t'\n+}\n+\n+test_bad_atom head 'authoremail:foo' \\\n+\t'fatal: unrecognized %(authoremail) argument: foo'\n+\n+test_bad_atom head 'authoremail:mailmap,trim,bar' \\\n+\t'fatal: unrecognized %(authoremail) argument: bar'\n+\n+test_bad_atom head 'authoremail:trim,' \\\n+\t'fatal: unrecognized %(authoremail) argument: '\n+\n+test_bad_atom head 'authoremail:mailmaptrim' \\\n+\t'fatal: unrecognized %(authoremail) argument: trim'\n+\n+test_bad_atom head 'committeremail: ' \\\n+\t'fatal: unrecognized %(committeremail) argument:  '\n+\n+test_bad_atom head 'committeremail: trim,foo' \\\n+\t'fatal: unrecognized %(committeremail) argument:  trim,foo'\n+\n+test_bad_atom head 'committeremail:mailmap,localpart ' \\\n+\t'fatal: unrecognized %(committeremail) argument:  '\n+\n+test_bad_atom head 'committeremail:trim_localpart' \\\n+\t'fatal: unrecognized %(committeremail) argument: _localpart'\n+\n+test_bad_atom head 'committeremail:localpart,,,trim' \\\n+\t'fatal: unrecognized %(committeremail) argument: ,,trim'\n+\n+test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \\\n+\t'fatal: unrecognized %(taggeremail) argument:  foo '\n+\n+test_bad_atom tag 'taggeremail:trim,localpart,' \\\n+\t'fatal: unrecognized %(taggeremail) argument: '\n+\n+test_bad_atom tag 'taggeremail:mailmap;localpart trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument: ;localpart trim'\n+\n+test_bad_atom tag 'taggeremail:localpart trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument:  trim'\n+\n+test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'\n+\n+test_date () {\n+\tf=$1 &&\n+\tcommitter_date=$2 &&\n+\tauthor_date=$3 &&\n+\ttagger_date=$4 &&\n+\tcat >expected <<-EOF &&\n+\t'refs/heads/main' '$committer_date' '$author_date'\n+\t'refs/tags/testtag' '$tagger_date'\n+\tEOF\n+\t(\n+\t\t${git_for_each_ref} --shell \\\n+\t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n+\t\t\trefs/heads &&\n+\t\t${git_for_each_ref} --shell \\\n+\t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n+\t\t\trefs/tags\n+\t) >actual &&\n+\ttest_cmp expected actual\n+}\n+\n+test_expect_success 'Check unformatted date fields output' '\n+\ttest_date \"\" \\\n+\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n+'\n+\n+test_expect_success 'Check format \"default\" formatted date fields output' '\n+\ttest_date default \\\n+\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n+'\n+\n+test_expect_success 'Check format \"default-local\" date fields output' '\n+\ttest_date default-local \"Mon Jul 3 23:18:43 2006\" \"Mon Jul 3 23:18:44 2006\" \"Mon Jul 3 23:18:45 2006\"\n+'\n+\n+# Don't know how to do relative check because I can't know when this script\n+# is going to be run and can't fake the current time to git, and hence can't\n+# provide expected output.  Instead, I'll just make sure that \"relative\"\n+# doesn't exit in error\n+test_expect_success 'Check format \"relative\" date fields output' '\n+\tf=relative &&\n+\t(${git_for_each_ref} --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n+\t${git_for_each_ref} --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n+'\n+\n+# We just check that this is the same as \"relative\" for now.\n+test_expect_success 'Check format \"relative-local\" date fields output' '\n+\ttest_date relative-local \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(committerdate:relative)\" refs/heads)\" \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(authordate:relative)\" refs/heads)\" \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(taggerdate:relative)\" refs/tags)\"\n+'\n+\n+test_expect_success 'Check format \"short\" date fields output' '\n+\ttest_date short 2006-07-04 2006-07-04 2006-07-04\n+'\n+\n+test_expect_success 'Check format \"short-local\" date fields output' '\n+\ttest_date short-local 2006-07-03 2006-07-03 2006-07-03\n+'\n+\n+test_expect_success 'Check format \"local\" date fields output' '\n+\ttest_date local \\\n+\t\t\"Mon Jul 3 23:18:43 2006\" \\\n+\t\t\"Mon Jul 3 23:18:44 2006\" \\\n+\t\t\"Mon Jul 3 23:18:45 2006\"\n+'\n+\n+test_expect_success 'Check format \"iso8601\" date fields output' '\n+\ttest_date iso8601 \\\n+\t\t\"2006-07-04 01:18:43 +0200\" \\\n+\t\t\"2006-07-04 01:18:44 +0200\" \\\n+\t\t\"2006-07-04 01:18:45 +0200\"\n+'\n+\n+test_expect_success 'Check format \"iso8601-local\" date fields output' '\n+\ttest_date iso8601-local \"2006-07-03 23:18:43 +0000\" \"2006-07-03 23:18:44 +0000\" \"2006-07-03 23:18:45 +0000\"\n+'\n+\n+test_expect_success 'Check format \"rfc2822\" date fields output' '\n+\ttest_date rfc2822 \\\n+\t\t\"Tue, 4 Jul 2006 01:18:43 +0200\" \\\n+\t\t\"Tue, 4 Jul 2006 01:18:44 +0200\" \\\n+\t\t\"Tue, 4 Jul 2006 01:18:45 +0200\"\n+'\n+\n+test_expect_success 'Check format \"rfc2822-local\" date fields output' '\n+\ttest_date rfc2822-local \"Mon, 3 Jul 2006 23:18:43 +0000\" \"Mon, 3 Jul 2006 23:18:44 +0000\" \"Mon, 3 Jul 2006 23:18:45 +0000\"\n+'\n+\n+test_expect_success 'Check format \"raw\" date fields output' '\n+\ttest_date raw \"1151968723 +0200\" \"1151968724 +0200\" \"1151968725 +0200\"\n+'\n+\n+test_expect_success 'Check format \"raw-local\" date fields output' '\n+\ttest_date raw-local \"1151968723 +0000\" \"1151968724 +0000\" \"1151968725 +0000\"\n+'\n+\n+test_expect_success 'Check format of strftime date fields' '\n+\techo \"my date is 2006-07-04\" >expected &&\n+\t${git_for_each_ref} \\\n+\t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n+\t  refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check format of strftime-local date fields' '\n+\techo \"my date is 2006-07-03\" >expected &&\n+\t${git_for_each_ref} \\\n+\t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n+\t  refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'exercise strftime with odd fields' '\n+\techo >expected &&\n+\t${git_for_each_ref} --format=\"%(authordate:format:)\" refs/heads >actual &&\n+\ttest_cmp expected actual &&\n+\tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n+\techo $long >expected &&\n+\t${git_for_each_ref} --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/heads/main\n+refs/remotes/origin/main\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'Verify ascending sort' '\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=refname >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/remotes/origin/main\n+refs/heads/main\n+EOF\n+\n+test_expect_success 'Verify descending sort' '\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=-refname >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Give help even with invalid sort atoms' '\n+\ttest_expect_code 129 ${git_for_each_ref} --sort=bogus -h >actual 2>&1 &&\n+\tgrep \"^usage: ${git_for_each_ref}\" actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/tags/testtag-2\n+EOF\n+\n+test_expect_success 'exercise patterns with prefixes' '\n+\tgit tag testtag-2 &&\n+\ttest_when_finished \"git tag -d testtag-2\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/tags/testtag-2\n+EOF\n+\n+test_expect_success 'exercise glob patterns with prefixes' '\n+\tgit tag testtag-2 &&\n+\ttest_when_finished \"git tag -d testtag-2\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/bar\n+refs/tags/baz\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'exercise patterns with prefix exclusions' '\n+\tfor tag in foo/one foo/two foo/three bar baz\n+\tdo\n+\t\tgit tag \"$tag\" || return 1\n+\tdone &&\n+\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/bar\n+refs/tags/baz\n+refs/tags/foo/one\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'exercise patterns with pattern exclusions' '\n+\tfor tag in foo/one foo/two foo/three bar baz\n+\tdo\n+\t\tgit tag \"$tag\" || return 1\n+\tdone &&\n+\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+'refs/heads/main'\n+'refs/remotes/origin/main'\n+'refs/tags/testtag'\n+EOF\n+\n+test_expect_success 'Quoting style: shell' '\n+\t${git_for_each_ref} --shell --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Quoting style: perl' '\n+\t${git_for_each_ref} --perl --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Quoting style: python' '\n+\t${git_for_each_ref} --python --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+\"refs/heads/main\"\n+\"refs/remotes/origin/main\"\n+\"refs/tags/testtag\"\n+EOF\n+\n+test_expect_success 'Quoting style: tcl' '\n+\t${git_for_each_ref} --tcl --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n+\ttest_expect_success \"more than one quoting style: $i\" \"\n+\t\ttest_must_fail ${git_for_each_ref} $i 2>err &&\n+\t\tgrep '^error: more than one quoting style' err\n+\t\"\n+done\n+\n+test_expect_success 'setup for upstream:track[short]' '\n+\ttest_commit two\n+'\n+\n+test_atom head upstream:track '[ahead 1]'\n+test_atom head upstream:trackshort '>'\n+test_atom head upstream:track,nobracket 'ahead 1'\n+test_atom head upstream:nobracket,track 'ahead 1'\n+\n+test_expect_success 'setup for push:track[short]' '\n+\ttest_commit third &&\n+\tgit update-ref refs/remotes/myfork/main main &&\n+\tgit reset main~1\n+'\n+\n+test_atom head push:track '[behind 1]'\n+test_atom head push:trackshort '<'\n+\n+test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:track)\" 2>/dev/null &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:trackshort)\" 2>/dev/null\n+'\n+\n+test_expect_success 'Check that :track[short] works when upstream is invalid' '\n+\tcat >expected <<-\\EOF &&\n+\t[gone]\n+\n+\tEOF\n+\ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n+\tgit config branch.main.merge refs/heads/does-not-exist &&\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n+\t\trefs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check for invalid refname format' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:INVALID)\"\n+'\n+\n+test_expect_success 'set up color tests' '\n+\tcat >expected.color <<-EOF &&\n+\t$(git rev-parse --short refs/heads/main) <GREEN>main<RESET>\n+\t$(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>\n+\t$(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>\n+\t$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>\n+\t$(git rev-parse --short refs/tags/third) <GREEN>third<RESET>\n+\t$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>\n+\tEOF\n+\tsed \"s/<[^>]*>//g\" <expected.color >expected.bare &&\n+\tcolor_format=\"%(objectname:short) %(color:green)%(refname:short)\"\n+'\n+\n+test_expect_success TTY '%(color) shows color with a tty' '\n+\ttest_terminal ${git_for_each_ref} --format=\"$color_format\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\ttest_cmp expected.color actual\n+'\n+\n+test_expect_success '%(color) does not show color without tty' '\n+\tTERM=vt100 ${git_for_each_ref} --format=\"$color_format\" >actual &&\n+\ttest_cmp expected.bare actual\n+'\n+\n+test_expect_success '--color can override tty check' '\n+\t${git_for_each_ref} --color --format=\"$color_format\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\ttest_cmp expected.color actual\n+'\n+\n+test_expect_success 'color.ui=always does not override tty check' '\n+\tgit -c color.ui=always ${git_for_each_ref#git} --format=\"$color_format\" >actual &&\n+\ttest_cmp expected.bare actual\n+'\n+\n+test_expect_success 'setup for describe atom tests' '\n+\tgit init -b master describe-repo &&\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\ttest_commit --no-tag one &&\n+\t\tgit tag tagone &&\n+\n+\t\ttest_commit --no-tag two &&\n+\t\tgit tag -a -m \"tag two\" tagtwo\n+\t)\n+'\n+\n+test_expect_success 'describe atom vs git describe' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t${git_for_each_ref} --format=\"%(objectname)\" \\\n+\t\t\trefs/tags/ >obj &&\n+\t\twhile read hash\n+\t\tdo\n+\t\t\tif desc=$(git describe $hash)\n+\t\t\tthen\n+\t\t\t\t: >expect-contains-good\n+\t\t\telse\n+\t\t\t\t: >expect-contains-bad\n+\t\t\tfi &&\n+\t\t\techo \"$hash $desc\" || return 1\n+\t\tdone <obj >expect &&\n+\t\ttest_path_exists expect-contains-good &&\n+\t\ttest_path_exists expect-contains-bad &&\n+\n+\t\t${git_for_each_ref} --format=\"%(objectname) %(describe)\" \\\n+\t\t\trefs/tags/ >actual 2>err &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest_must_be_empty err\n+\t)\n+'\n+\n+test_expect_success 'describe:tags vs describe --tags' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit describe --tags >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:tags)\" \\\n+\t\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t# Case 1: We have commits between HEAD and the most\n+\t\t#\t  recent tag reachable from it\n+\t\ttest_commit --no-tag file &&\n+\t\tgit describe --abbrev=14 >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:abbrev=14)\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# Make sure the hash used is at least 14 digits long\n+\t\tsed -e \"s/^.*-g\\([0-9a-f]*\\)$/\\1/\" <actual >hexpart &&\n+\t\ttest 15 -le $(wc -c <hexpart) &&\n+\n+\t\t# Case 2: We have a tag at HEAD, describe directly gives\n+\t\t#\t  the name of the tag\n+\t\tgit tag -a -m tagged tagname &&\n+\t\tgit describe --abbrev=14 >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:abbrev=14)\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest tagname = $(cat actual)\n+\t)\n+'\n+\n+test_expect_success 'describe:match=... vs describe --match ...' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit tag -a -m \"tag foo\" tag-foo &&\n+\t\tgit describe --match \"*-foo\" >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:match=\"*-foo\")\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit tag -a -m \"tag bar\" tag-bar &&\n+\t\tgit describe --exclude \"*-bar\" >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:exclude=\"*-bar\")\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'deref with describe atom' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tcat >expect <<-\\EOF &&\n+\n+\t\ttagname\n+\t\ttagname\n+\t\ttagname\n+\n+\t\ttagtwo\n+\t\tEOF\n+\t\t${git_for_each_ref} --format=\"%(*describe)\" >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'err on bad describe atom arg' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t# The bad arg is the only arg passed to describe atom\n+\t\tcat >expect <<-\\EOF &&\n+\t\tfatal: unrecognized %(describe) argument: baz\n+\t\tEOF\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%(describe:baz)\" \\\n+\t\t\trefs/heads/master 2>actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# The bad arg is in the middle of the option string\n+\t\t# passed to the describe atom\n+\t\tcat >expect <<-\\EOF &&\n+\t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n+\t\tEOF\n+\t\ttest_must_fail ${git_for_each_ref} \\\n+\t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n+\t\t\tref/heads/master 2>actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+cat >expected <<\\EOF\n+heads/main\n+tags/main\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs (strict)' '\n+\tgit config --bool core.warnambiguousrefs true &&\n+\tgit checkout -b newtag &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Branch\" &&\n+\tsetdate_and_increment &&\n+\tgit tag -m \"Tagging at $datestamp\" main &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/main\n+main\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs (loose)' '\n+\tgit config --bool core.warnambiguousrefs false &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/ambiguous\n+ambiguous\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n+\tgit checkout main &&\n+\tgit tag ambiguous testtag^0 &&\n+\tgit branch ambiguous testtag^0 &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create tag without tagger' '\n+\tgit tag -a -m \"Broken tag\" taggerless &&\n+\tgit tag -f taggerless $(git cat-file tag taggerless |\n+\t\tsed -e \"/^tagger /d\" |\n+\t\tgit hash-object --literally --stdin -w -t tag)\n+'\n+\n+test_atom refs/tags/taggerless type 'commit'\n+test_atom refs/tags/taggerless tag 'taggerless'\n+test_atom refs/tags/taggerless tagger ''\n+test_atom refs/tags/taggerless taggername ''\n+test_atom refs/tags/taggerless taggeremail ''\n+test_atom refs/tags/taggerless taggeremail:trim ''\n+test_atom refs/tags/taggerless taggeremail:localpart ''\n+test_atom refs/tags/taggerless taggerdate ''\n+test_atom refs/tags/taggerless committer ''\n+test_atom refs/tags/taggerless committername ''\n+test_atom refs/tags/taggerless committeremail ''\n+test_atom refs/tags/taggerless committeremail:trim ''\n+test_atom refs/tags/taggerless committeremail:localpart ''\n+test_atom refs/tags/taggerless committerdate ''\n+test_atom refs/tags/taggerless subject 'Broken tag'\n+\n+test_expect_success 'an unusual tag with an incomplete line' '\n+\n+\tgit tag -m \"bogo\" bogo &&\n+\tbogo=$(git cat-file tag bogo) &&\n+\tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n+\tgit tag -f bogo \"$bogo\" &&\n+\t${git_for_each_ref} --format \"%(body)\" refs/tags/bogo\n+\n+'\n+\n+test_expect_success 'create tag with subject and body content' '\n+\tcat >>msg <<-\\EOF &&\n+\t\tthe subject line\n+\n+\t\tfirst body line\n+\t\tsecond body line\n+\tEOF\n+\tgit tag -F msg subject-body\n+'\n+test_atom refs/tags/subject-body subject 'the subject line'\n+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n+test_atom refs/tags/subject-body body 'first body line\n+second body line\n+'\n+test_atom refs/tags/subject-body contents 'the subject line\n+\n+first body line\n+second body line\n+'\n+\n+test_expect_success 'create tag with multiline subject' '\n+\tcat >msg <<-\\EOF &&\n+\t\tfirst subject line\n+\t\tsecond subject line\n+\n+\t\tfirst body line\n+\t\tsecond body line\n+\tEOF\n+\tgit tag -F msg multiline\n+'\n+test_atom refs/tags/multiline subject 'first subject line second subject line'\n+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n+test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n+test_atom refs/tags/multiline body 'first body line\n+second body line\n+'\n+test_atom refs/tags/multiline contents:body 'first body line\n+second body line\n+'\n+test_atom refs/tags/multiline contents:signature ''\n+test_atom refs/tags/multiline contents 'first subject line\n+second subject line\n+\n+first body line\n+second body line\n+'\n+\n+test_expect_success GPG 'create signed tags' '\n+\tgit tag -s -m \"\" signed-empty &&\n+\tgit tag -s -m \"subject line\" signed-short &&\n+\tcat >msg <<-\\EOF &&\n+\tsubject line\n+\n+\tbody contents\n+\tEOF\n+\tgit tag -s -F msg signed-long\n+'\n+\n+sig='-----BEGIN PGP SIGNATURE-----\n+-----END PGP SIGNATURE-----\n+'\n+\n+PREREQ=GPG\n+test_atom refs/tags/signed-empty subject ''\n+test_atom refs/tags/signed-empty subject:sanitize ''\n+test_atom refs/tags/signed-empty contents:subject ''\n+test_atom refs/tags/signed-empty body \"$sig\"\n+test_atom refs/tags/signed-empty contents:body ''\n+test_atom refs/tags/signed-empty contents:signature \"$sig\"\n+test_atom refs/tags/signed-empty contents \"$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n+\tgit cat-file tag refs/tags/signed-empty >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_atom refs/tags/signed-short subject 'subject line'\n+test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n+test_atom refs/tags/signed-short contents:subject 'subject line'\n+test_atom refs/tags/signed-short body \"$sig\"\n+test_atom refs/tags/signed-short contents:body ''\n+test_atom refs/tags/signed-short contents:signature \"$sig\"\n+test_atom refs/tags/signed-short contents \"subject line\n+$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n+\tgit cat-file tag refs/tags/signed-short >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-short >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_atom refs/tags/signed-long subject 'subject line'\n+test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n+test_atom refs/tags/signed-long contents:subject 'subject line'\n+test_atom refs/tags/signed-long body \"body contents\n+$sig\"\n+test_atom refs/tags/signed-long contents:body 'body contents\n+'\n+test_atom refs/tags/signed-long contents:signature \"$sig\"\n+test_atom refs/tags/signed-long contents \"subject line\n+\n+body contents\n+$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n+\tgit cat-file tag refs/tags/signed-long >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-long >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_expect_success 'set up refs pointing to tree and blob' '\n+\tgit update-ref refs/mytrees/first refs/heads/main^{tree} &&\n+\tgit update-ref refs/myblobs/first refs/heads/main:one\n+'\n+\n+test_atom refs/mytrees/first subject \"\"\n+test_atom refs/mytrees/first contents:subject \"\"\n+test_atom refs/mytrees/first body \"\"\n+test_atom refs/mytrees/first contents:body \"\"\n+test_atom refs/mytrees/first contents:signature \"\"\n+test_atom refs/mytrees/first contents \"\"\n+\n+test_expect_success 'basic atom: refs/mytrees/first raw' '\n+\tgit cat-file tree refs/mytrees/first >expected &&\n+\techo >>expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file -s refs/mytrees/first >expected &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_atom refs/myblobs/first subject \"\"\n+test_atom refs/myblobs/first contents:subject \"\"\n+test_atom refs/myblobs/first body \"\"\n+test_atom refs/myblobs/first contents:body \"\"\n+test_atom refs/myblobs/first contents:signature \"\"\n+test_atom refs/myblobs/first contents \"\"\n+\n+test_expect_success 'basic atom: refs/myblobs/first raw' '\n+\tgit cat-file blob refs/myblobs/first >expected &&\n+\techo >>expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/myblobs/first >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file -s refs/myblobs/first >expected &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'set up refs pointing to binary blob' '\n+\tprintf \"a\\0b\\0c\" >blob1 &&\n+\tprintf \"a\\0c\\0b\" >blob2 &&\n+\tprintf \"\\0a\\0b\\0c\" >blob3 &&\n+\tprintf \"abc\" >blob4 &&\n+\tprintf \"\\0 \\0 \\0 \" >blob5 &&\n+\tprintf \"\\0 \\0a\\0 \" >blob6 &&\n+\tprintf \"  \" >blob7 &&\n+\t>blob8 &&\n+\tobj=$(git hash-object -w blob1) &&\n+\tgit update-ref refs/myblobs/blob1 \"$obj\" &&\n+\tobj=$(git hash-object -w blob2) &&\n+\tgit update-ref refs/myblobs/blob2 \"$obj\" &&\n+\tobj=$(git hash-object -w blob3) &&\n+\tgit update-ref refs/myblobs/blob3 \"$obj\" &&\n+\tobj=$(git hash-object -w blob4) &&\n+\tgit update-ref refs/myblobs/blob4 \"$obj\" &&\n+\tobj=$(git hash-object -w blob5) &&\n+\tgit update-ref refs/myblobs/blob5 \"$obj\" &&\n+\tobj=$(git hash-object -w blob6) &&\n+\tgit update-ref refs/myblobs/blob6 \"$obj\" &&\n+\tobj=$(git hash-object -w blob7) &&\n+\tgit update-ref refs/myblobs/blob7 \"$obj\" &&\n+\tobj=$(git hash-object -w blob8) &&\n+\tgit update-ref refs/myblobs/blob8 \"$obj\"\n+'\n+\n+test_expect_success 'Verify sorts with raw' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob8\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/blob3\n+\trefs/myblobs/blob7\n+\trefs/mytrees/first\n+\trefs/myblobs/first\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob4\n+\trefs/heads/main\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=raw \\\n+\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Verify sorts with raw:size' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob8\n+\trefs/myblobs/blob7\n+\trefs/myblobs/blob4\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob3\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/first\n+\trefs/mytrees/first\n+\trefs/heads/main\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=raw:size \\\n+\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'validate raw atom with %(if:equals)' '\n+\tcat >expected <<-EOF &&\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\trefs/myblobs/blob4\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n+\t\trefs/myblobs/ refs/heads/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'validate raw atom with %(if:notequals)' '\n+\tcat >expected <<-EOF &&\n+\trefs/heads/ambiguous\n+\trefs/heads/main\n+\trefs/heads/newtag\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob3\n+\tequals\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/blob7\n+\trefs/myblobs/blob8\n+\trefs/myblobs/first\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n+\t\trefs/myblobs/ refs/heads/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'empty raw refs with %(if)' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob1 not empty\n+\trefs/myblobs/blob2 not empty\n+\trefs/myblobs/blob3 not empty\n+\trefs/myblobs/blob4 not empty\n+\trefs/myblobs/blob5 not empty\n+\trefs/myblobs/blob6 not empty\n+\trefs/myblobs/blob7 empty\n+\trefs/myblobs/blob8 empty\n+\trefs/myblobs/first not empty\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n+\t\trefs/myblobs/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '%(raw) with --python must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --python\n+'\n+\n+test_expect_success '%(raw) with --tcl must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --tcl\n+'\n+\n+test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n+\tcmp blob1 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n+\tcmp blob3 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n+\tcmp blob8 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n+\tcmp one actual &&\n+\tgit cat-file tree refs/mytrees/first > expected &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n+\tcmp expected actual\n+'\n+\n+test_expect_success '%(raw) with --shell must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --shell\n+'\n+\n+test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --sort=raw --shell\n+'\n+\n+test_expect_success '%(raw:size) with --shell' '\n+\t${git_for_each_ref} --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" --shell >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --format compare with cat-file --batch\" '\n+\tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n+\t${git_for_each_ref} --format=\"%(objectname) %(objecttype) %(objectsize)\n+%(raw)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify sorts with contents:size' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/main\n+\trefs/heads/newtag\n+\trefs/heads/ambiguous\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\t--sort=contents:size refs/heads/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'set up multiple-sort tags' '\n+\tfor when in 100000 200000\n+\tdo\n+\t\tfor email in user1 user2\n+\t\tdo\n+\t\t\tfor ref in ref1 ref2\n+\t\t\tdo\n+\t\t\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n+\t\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n+\t\t\t\tgit tag -m \"tag $ref-$when-$email\" \\\n+\t\t\t\tmulti-$ref-$when-$email || return 1\n+\t\t\tdone\n+\t\tdone\n+\tdone\n+'\n+\n+test_expect_success 'Verify sort with multiple keys' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=-refname \\\n+\t\t--sort=taggeremail \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'equivalent sorts fall back on refname' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--no-sort cancels the previous sort keys' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=-refname \\\n+\t\t--sort=taggeremail \\\n+\t\t--no-sort \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n+\tcat >expected <<-\\EOF &&\n+\trefs/tags/multi-ref1-100000-user1\n+\trefs/tags/multi-ref1-100000-user2\n+\trefs/tags/multi-ref1-200000-user1\n+\trefs/tags/multi-ref1-200000-user2\n+\trefs/tags/multi-ref2-100000-user1\n+\trefs/tags/multi-ref2-100000-user2\n+\trefs/tags/multi-ref2-200000-user1\n+\trefs/tags/multi-ref2-200000-user2\n+\tEOF\n+\n+\t# Sort the results with `sort` for a consistent comparison against\n+\t# expected\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(refname)\" \\\n+\t\t--no-sort \\\n+\t\t\"refs/tags/multi-*\" | sort >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'set up custom date sorting' '\n+\t# Dates:\n+\t# - Wed Feb 07 2024 21:34:20 +0000\n+\t# - Tue Dec 14 1999 00:05:22 +0000\n+\t# - Fri Jun 04 2021 11:26:51 +0000\n+\t# - Mon Jan 22 2007 16:44:01 GMT+0000\n+\ti=1 &&\n+\tfor when in 1707341660 945129922 1622806011 1169484241\n+\tdo\n+\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n+\t\tGIT_COMMITTER_EMAIL=\"user@example.com\" \\\n+\t\tgit tag -m \"tag $when\" custom-dates-$i &&\n+\t\ti=$(($i+1)) || return 1\n+\tdone\n+'\n+\n+test_expect_success 'sort by date defaults to full timestamp' '\n+\tcat >expected <<-\\EOF &&\n+\t945129922 refs/tags/custom-dates-2\n+\t1169484241 refs/tags/custom-dates-4\n+\t1622806011 refs/tags/custom-dates-3\n+\t1707341660 refs/tags/custom-dates-1\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(creatordate:unix) %(refname)\" \\\n+\t\t--sort=creatordate \\\n+\t\t\"refs/tags/custom-dates-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'sort by custom date format' '\n+\tcat >expected <<-\\EOF &&\n+\t00:05:22 refs/tags/custom-dates-2\n+\t11:26:51 refs/tags/custom-dates-3\n+\t16:44:01 refs/tags/custom-dates-4\n+\t21:34:20 refs/tags/custom-dates-1\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n+\t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n+\t\t\"refs/tags/custom-dates-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n+\ttest_when_finished \"git checkout main\" &&\n+\t${git_for_each_ref} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\tsed -e \"s/^\\* /  /\" actual >expect &&\n+\tgit checkout --orphan orphaned-branch &&\n+\t${git_for_each_ref} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+cat >trailers <<EOF\n+Reviewed-by: A U Thor <author@example.com>\n+Signed-off-by: A U Thor <author@example.com>\n+[ v2 updated patch description ]\n+Acked-by: A U Thor\n+  <author@example.com>\n+EOF\n+\n+unfold () {\n+\tperl -0pe 's/\\n\\s+/ /g'\n+}\n+\n+test_expect_success 'set up trailers for next test' '\n+\techo \"Some contents\" > two &&\n+\tgit add two &&\n+\tgit commit -F - <<-EOF\n+\ttrailers: this commit message has trailers\n+\n+\tSome message contents\n+\n+\t$(cat trailers)\n+\tEOF\n+'\n+\n+test_trailer_option () {\n+\tif test \"$#\" -eq 3\n+\tthen\n+\t\tprereq=\"$1\"\n+\t\tshift\n+\tfi &&\n+\ttitle=$1 option=$2\n+\tcat >expect\n+\ttest_expect_success $prereq \"$title\" '\n+\t\t${git_for_each_ref} --format=\"%($option)\" refs/heads/main >actual &&\n+\t\ttest_cmp expect actual &&\n+\t\t${git_for_each_ref} --format=\"%(contents:$option)\" refs/heads/main >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \\\n+\t'trailers:unfold' <<-EOF\n+\t$(unfold <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only) shows only \"key: value\" trailers' \\\n+\t'trailers:only' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=no,only=true) shows only \"key: value\" trailers' \\\n+\t'trailers:only=no,only=true' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=yes) shows only \"key: value\" trailers' \\\n+\t'trailers:only=yes' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=no) shows all trailers' \\\n+\t'trailers:only=no' <<-EOF\n+\t$(cat trailers)\n+\n+\tEOF\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \\\n+\t'trailers:only,unfold' <<-EOF\n+\t$(grep -v patch.description <trailers | unfold)\n+\n+\tEOF\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \\\n+\t'trailers:unfold,only' <<-EOF\n+\t$(grep -v patch.description <trailers | unfold)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) shows that trailer' \\\n+\t'trailers:key=Signed-off-by' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) is case insensitive' \\\n+\t'trailers:key=SiGned-oFf-bY' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo:) trailing colon also works' \\\n+\t'trailers:key=Signed-off-by:' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) multiple keys' \\\n+\t'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF\n+\tReviewed-by: A U Thor <author@example.com>\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=nonexistent) becomes empty' \\\n+\t'trailers:key=Shined-off-by:' <<-EOF\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \\\n+\t'trailers:key=Acked-by' <<-EOF\n+\t$(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \\\n+\t'trailers:key=Signed-Off-by,unfold' <<-EOF\n+\t$(unfold <trailers | grep Signed-off-by)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \\\n+\t'trailers:key=Signed-off-by,only=no' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\t$(grep patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \\\n+\t'trailers:key=Signed-off-by,valueonly' <<-EOF\n+\tA U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:separator) changes separator' \\\n+\t'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>\n+\tEOF\n+\n+test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \\\n+\t'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by,A U Thor <author@example.com>\n+\tSigned-off-by,A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \\\n+\t'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>\n+\tEOF\n+\n+test_expect_success 'multiple %(trailers) use their own options' '\n+\tgit tag -F - tag-with-trailers <<-\\EOF &&\n+\tbody\n+\n+\tone: foo\n+\tone: bar\n+\ttwo: baz\n+\ttwo: qux\n+\tEOF\n+\tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n+\tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n+\t${git_for_each_ref} --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n+\tcat >expect <<-\\EOF &&\n+\toneWfooXoneWbar\n+\ttwoYbazZtwoYqux\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_failing_trailer_option () {\n+\ttitle=$1 option=$2\n+\tcat >expect\n+\ttest_expect_success \"$title\" '\n+\t\t# error message cannot be checked under i18n\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%($option)\" refs/heads/main 2>actual &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \\\n+\t'trailers:unsupported' <<-\\EOF\n+\tfatal: unknown %(trailers) argument: unsupported\n+\tEOF\n+\n+test_failing_trailer_option '%(trailers:key) without value is error' \\\n+\t'trailers:key' <<-\\EOF\n+\tfatal: expected %(trailers:key=<value>)\n+\tEOF\n+\n+test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '\n+\tcat >expect <<-EOF &&\n+\tfatal: unrecognized %(contents) argument: trailersonly\n+\tEOF\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(contents:trailersonly)\" 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'basic atom: head contents:trailers' '\n+\t${git_for_each_ref} --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\t# ${git_for_each_ref} ends with a blank line\n+\tcat >expect <<-EOF &&\n+\t$(cat trailers)\n+\n+\tEOF\n+\ttest_cmp expect actual.clean\n+'\n+\n+test_expect_success 'basic atom: rest must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(rest)\" refs/heads/main\n+'\n+\n+test_expect_success 'HEAD atom does not take arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(HEAD:foo)\" 2>err &&\n+\techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'subject atom rejects unknown arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(subject:foo)\" 2>err &&\n+\techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'refname atom rejects unknown arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:foo)\" 2>err &&\n+\techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'trailer parsing not fooled by --- line' '\n+\tgit commit --allow-empty -F - <<-\\EOF &&\n+\tthis is the subject\n+\n+\tThis is the body. The message has a \"---\" line which would confuse a\n+\tmessage+patch parser. But here we know we have only a commit message,\n+\tso we get it right.\n+\n+\ttrailer: wrong\n+\t---\n+\tThis is more body.\n+\n+\ttrailer: right\n+\tEOF\n+\n+\t{\n+\t\techo \"trailer: right\" &&\n+\t\techo\n+\t} >expect &&\n+\t${git_for_each_ref} --format=\"%(trailers)\" refs/heads/main >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'Add symbolic ref for the following tests' '\n+\tgit symbolic-ref refs/heads/sym refs/heads/main\n+'\n+\n+cat >expected <<EOF\n+refs/heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref) atom' '\n+\t${git_for_each_ref} --format=\"%(symref)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:short) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:short)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+main\n+heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual &&\n+\n+\t${git_for_each_ref} --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+refs\n+refs/heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success ':remotename and :remoteref' '\n+\tgit init remote-tests &&\n+\t(\n+\t\tcd remote-tests &&\n+\t\ttest_commit initial &&\n+\t\tgit branch -M main &&\n+\t\tgit remote add from fifth.coffee:blub &&\n+\t\tgit config branch.main.remote from &&\n+\t\tgit config branch.main.merge refs/heads/stable &&\n+\t\tgit remote add to southridge.audio:repo &&\n+\t\tgit config remote.to.push \"refs/heads/*:refs/heads/pushed/*\" &&\n+\t\tgit config branch.main.pushRemote to &&\n+\t\tfor pair in \"%(upstream)=refs/remotes/from/stable\" \\\n+\t\t\t\"%(upstream:remotename)=from\" \\\n+\t\t\t\"%(upstream:remoteref)=refs/heads/stable\" \\\n+\t\t\t\"%(push)=refs/remotes/to/pushed/main\" \\\n+\t\t\t\"%(push:remotename)=to\" \\\n+\t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n+\t\tdo\n+\t\t\techo \"${pair#*=}\" >expect &&\n+\t\t\t${git_for_each_ref} --format=\"${pair%=*}\" \\\n+\t\t\t\trefs/heads/main >actual &&\n+\t\t\ttest_cmp expect actual || exit 1\n+\t\tdone &&\n+\t\tgit branch push-simple &&\n+\t\tgit config branch.push-simple.pushRemote from &&\n+\t\tactual=\"$(${git_for_each_ref} \\\n+\t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n+\t\t\trefs/heads/push-simple)\" &&\n+\t\ttest from, = \"$actual\"\n+\t)\n+'\n+\n+test_expect_success \"${git_for_each_ref} --ignore-case ignores case\" '\n+\t${git_for_each_ref} --format=\"%(refname)\" refs/heads/MAIN >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\techo refs/heads/main >expect &&\n+\t${git_for_each_ref} --format=\"%(refname)\" --ignore-case \\\n+\t\trefs/heads/MAIN >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --omit-empty works\" '\n+\t${git_for_each_ref} --format=\"%(refname)\" >actual &&\n+\ttest_line_count -gt 1 actual &&\n+\t${git_for_each_ref} --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n+\techo refs/heads/main >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --ignore-case works on multiple sort keys\" '\n+\t# name refs numerically to avoid case-insensitive filesystem conflicts\n+\tnr=0 &&\n+\tfor email in a A b B\n+\tdo\n+\t\tfor subject in a A b B\n+\t\tdo\n+\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n+\t\t\tgit tag -m \"tag $subject\" icase-$(printf %02d $nr) &&\n+\t\t\tnr=$((nr+1))||\n+\t\t\treturn 1\n+\t\tdone\n+\tdone &&\n+\t${git_for_each_ref} --ignore-case \\\n+\t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n+\t\t--sort=refname \\\n+\t\t--sort=subject \\\n+\t\t--sort=taggeremail \\\n+\t\trefs/tags/icase-* >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t<a@example.com> tag a refs/tags/icase-00\n+\t<a@example.com> tag A refs/tags/icase-01\n+\t<A@example.com> tag a refs/tags/icase-04\n+\t<A@example.com> tag A refs/tags/icase-05\n+\t<a@example.com> tag b refs/tags/icase-02\n+\t<a@example.com> tag B refs/tags/icase-03\n+\t<A@example.com> tag b refs/tags/icase-06\n+\t<A@example.com> tag B refs/tags/icase-07\n+\t<b@example.com> tag a refs/tags/icase-08\n+\t<b@example.com> tag A refs/tags/icase-09\n+\t<B@example.com> tag a refs/tags/icase-12\n+\t<B@example.com> tag A refs/tags/icase-13\n+\t<b@example.com> tag b refs/tags/icase-10\n+\t<b@example.com> tag B refs/tags/icase-11\n+\t<B@example.com> tag b refs/tags/icase-14\n+\t<B@example.com> tag B refs/tags/icase-15\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} reports broken tags\" '\n+\tgit tag -m \"good tag\" broken-tag-good HEAD &&\n+\tgit cat-file tag broken-tag-good >good &&\n+\tsed s/commit/blob/ <good >bad &&\n+\tbad=$(git hash-object -w -t tag bad) &&\n+\tgit update-ref refs/tags/broken-tag-bad $bad &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(*objectname)\" \\\n+\t\trefs/tags/broken-tag-*\n+'\n+\n+test_expect_success 'set up tag with signature and no blank lines' '\n+\tgit tag -F - fake-sig-no-blanks <<-\\EOF\n+\tthis is the subject\n+\t-----BEGIN PGP SIGNATURE-----\n+\tnot a real signature, but we just care about the\n+\tsubject/body parsing. It is important here that\n+\tthere are no blank lines in the signature.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+'\n+\n+test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'\n+test_atom refs/tags/fake-sig-no-blanks contents:body ''\n+test_atom refs/tags/fake-sig-no-blanks contents:signature \"$sig\"\n+\n+test_expect_success 'set up tag with CRLF signature' '\n+\tappend_cr <<-\\EOF |\n+\tthis is the subject\n+\t-----BEGIN PGP SIGNATURE-----\n+\n+\tnot a real signature, but we just care about\n+\tthe subject/body parsing. It is important here\n+\tthat there is a blank line separating this\n+\tfrom the signature header.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+\tgit tag -F - --cleanup=verbatim fake-sig-crlf\n+'\n+\n+test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'\n+test_atom refs/tags/fake-sig-crlf contents:body ''\n+\n+# CRLF is retained in the signature, so we have to pass our expected value\n+# through append_cr. But test_atom requires a shell string, which means command\n+# substitution, and the shell will strip trailing newlines from the output of\n+# the substitution. Hack around it by adding and then removing a dummy line.\n+sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n+sig_crlf=${sig_crlf%dummy}\n+test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n+\n+test_expect_success 'set up tag with signature and trailers' '\n+\tgit tag -F - fake-sig-trailer <<-\\EOF\n+\tthis is the subject\n+\n+\tthis is the body\n+\n+\tMy-Trailer: foo\n+\t-----BEGIN PGP SIGNATURE-----\n+\n+\tnot a real signature, but we just care about the\n+\tsubject/body/trailer parsing.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+'\n+\n+# use \"separator=\" here to suppress the terminating newline\n+test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: empty\" '\n+\t>in &&\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\t${git_for_each_ref} --format=\"%(refname)\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: fails if extra args\" '\n+\t>in &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\t--stdin refs/heads/extra <in 2>err &&\n+\tgrep \"unknown arguments supplied with --stdin\" err\n+'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: matches\" '\n+\tcat >in <<-EOF &&\n+\trefs/tags/multi*\n+\trefs/heads/amb*\n+\tEOF\n+\n+\tcat >expect <<-EOF &&\n+\trefs/heads/ambiguous\n+\trefs/tags/multi-ref1-100000-user1\n+\trefs/tags/multi-ref1-100000-user2\n+\trefs/tags/multi-ref1-200000-user1\n+\trefs/tags/multi-ref1-200000-user2\n+\trefs/tags/multi-ref2-100000-user1\n+\trefs/tags/multi-ref2-100000-user2\n+\trefs/tags/multi-ref2-200000-user1\n+\trefs/tags/multi-ref2-200000-user2\n+\trefs/tags/multiline\n+\tEOF\n+\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} with non-existing refs\" '\n+\tcat >in <<-EOF &&\n+\trefs/heads/this-ref-does-not-exist\n+\trefs/tags/bogus\n+\tEOF\n+\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\txargs ${git_for_each_ref} --format=\"%(refname)\" <in >actual &&\n+\ttest_must_be_empty actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} with nested tags\" '\n+\tgit tag -am \"Normal tag\" nested/base HEAD &&\n+\tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n+\tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n+\n+\thead_oid=\"$(git rev-parse HEAD)\" &&\n+\tbase_tag_oid=\"$(git rev-parse refs/tags/nested/base)\" &&\n+\tnest1_tag_oid=\"$(git rev-parse refs/tags/nested/nest1)\" &&\n+\tnest2_tag_oid=\"$(git rev-parse refs/tags/nested/nest2)\" &&\n+\n+\tcat >expect <<-EOF &&\n+\trefs/tags/nested/base $base_tag_oid tag $head_oid commit\n+\trefs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit\n+\trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n+\t\trefs/tags/nested/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'is-base atom with non-commits' '\n+\t${git_for_each_ref} --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n+\tgrep \"(HEAD) refs/heads/main\" out &&\n+\n+\ttest_line_count = 2 err &&\n+\tgrep \"error: object .* is a commit, not a blob\" err &&\n+\tgrep \"error: bad tag pointer to\" err\n+'\n+\n+GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+\n+test_expect_success GPG 'setup for signature atom using gpg' '\n+\tgit checkout -b signed &&\n+\n+\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n+\n+\techo \"1\" >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -S -m \"file: 1\" &&\n+\tgit tag first-signed &&\n+\n+\techo \"2\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -m \"file: 2\" &&\n+\tgit tag second-unsigned &&\n+\n+\tgit config commit.gpgSign 1 &&\n+\techo \"3\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n+\tgit tag third-unsigned &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n+\tgit tag third-signed &&\n+\n+\techo \"4\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -SB7227189 -m \"file: 4\" &&\n+\tgit tag fourth-signed &&\n+\n+\techo \"5\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n+\tgit tag fifth-unsigned &&\n+\n+\techo \"6\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ &&\n+\tgit tag fifth-signed HEAD^ &&\n+\tgit tag sixth-signed &&\n+\n+\techo \"7\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n+\tgit tag seventh-unsigned\n+'\n+\n+test_expect_success GPGSSH 'setup for signature atom using ssh' '\n+\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n+\n+\ttest_config gpg.format ssh &&\n+\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n+\techo \"8\" >file &&\n+\ttest_tick &&\n+\tgit add file &&\n+\tgit commit -S -m \"file: 8\" &&\n+\tgit tag eighth-signed-ssh\n+'\n+\n+test_expect_success GPG2 'bare signature atom' '\n+\tgit verify-commit first-signed 2>expect &&\n+\techo  >>expect &&\n+\t${git_for_each_ref} refs/tags/first-signed \\\n+\t\t--format=\"%(signature)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show good signature with custom format' '\n+\tgit verify-commit first-signed &&\n+\tcat >expect <<-\\EOF &&\n+\tG\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\t${git_for_each_ref} refs/tags/first-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n+\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n+\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n+\tcat >expect.tmpl <<-\\EOF &&\n+\tG\n+\tFINGERPRINT\n+\tprincipal with number 1\n+\tFINGERPRINT\n+\n+\tEOF\n+\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n+\t${git_for_each_ref} refs/tags/eighth-signed-ssh \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'signature atom with grade option and bad signature' '\n+\tgit cat-file commit third-signed >raw &&\n+\tsed -e \"s/^file: 3/file: 3 forged/\" raw >forged1 &&\n+\tFORGED1=$(git hash-object -w -t commit forged1) &&\n+\tgit update-ref refs/tags/third-signed \"$FORGED1\" &&\n+\ttest_must_fail git verify-commit \"$FORGED1\" &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tB\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\n+\n+\tEOF\n+\t${git_for_each_ref} refs/tags/third-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tU\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\t${git_for_each_ref} refs/tags/fourth-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with undefined trust level' '\n+\tcat >expect <<-\\EOF &&\n+\tundefined\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\t${git_for_each_ref} refs/tags/fourth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n+\tcat >expect <<-\\EOF &&\n+\tultimate\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\t${git_for_each_ref} refs/tags/sixth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show unknown signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tE\n+\t13B6F51ECDDE430D\n+\n+\n+\n+\tEOF\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" ${git_for_each_ref} \\\n+\t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show lack of signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tN\n+\n+\n+\n+\n+\tEOF\n+\t${git_for_each_ref} refs/tags/seventh-unsigned \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex ce9af79ab1..1d9809114d 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -6,2150 +6,14 @@\n test_description='for-each-ref test'\n \n . ./test-lib.sh\n-GNUPGHOME_NOT_USED=$GNUPGHOME\n-. \"$TEST_DIRECTORY\"/lib-gpg.sh\n-. \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n-# Mon Jul 3 23:18:43 2006 +0000\n-datestamp=1151968723\n-setdate_and_increment () {\n-    GIT_COMMITTER_DATE=\"$datestamp +0200\"\n-    datestamp=$(expr \"$datestamp\" + 1)\n-    GIT_AUTHOR_DATE=\"$datestamp +0200\"\n-    datestamp=$(expr \"$datestamp\" + 1)\n-    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n-}\n-\n-test_object_file_size () {\n-\toid=$(git rev-parse \"$1\")\n-\tpath=\".git/objects/$(test_oid_to_path $oid)\"\n-\ttest_file_size \"$path\"\n-}\n-\n-test_expect_success setup '\n-\t# setup .mailmap\n-\tcat >.mailmap <<-EOF &&\n-\tA Thor <athor@example.com> A U Thor <author@example.com>\n-\tC Mitter <cmitter@example.com> C O Mitter <committer@example.com>\n-\tEOF\n-\n-\tsetdate_and_increment &&\n-\techo \"Using $datestamp\" > one &&\n-\tgit add one &&\n-\tgit commit -m \"Initial\" &&\n-\tgit branch -M main &&\n-\tsetdate_and_increment &&\n-\tgit tag -a -m \"Tagging at $datestamp\" testtag &&\n-\tgit update-ref refs/remotes/origin/main main &&\n-\tgit remote add origin nowhere &&\n-\tgit config branch.main.remote origin &&\n-\tgit config branch.main.merge refs/heads/main &&\n-\tgit remote add myfork elsewhere &&\n-\tgit config remote.pushdefault myfork &&\n-\tgit config push.default current\n-'\n-\n-test_atom () {\n-\tcase \"$1\" in\n-\t\thead) ref=refs/heads/main ;;\n-\t\t tag) ref=refs/tags/testtag ;;\n-\t\t sym) ref=refs/heads/sym ;;\n-\t\t   *) ref=$1 ;;\n-\tesac\n-\tformat=$2\n-\ttest_do=test_expect_${4:-success}\n-\n-\tprintf '%s\\n' \"$3\" >expected\n-\t$test_do $PREREQ \"basic atom: $ref $format\" '\n-\t\tgit for-each-ref --format=\"%($format)\" \"$ref\" >actual &&\n-\t\tsanitize_pgp <actual >actual.clean &&\n-\t\ttest_cmp expected actual.clean\n-\t'\n-\n-\t# Automatically test \"contents:size\" atom after testing \"contents\"\n-\tif test \"$format\" = \"contents\"\n-\tthen\n-\t\t# for commit leg, $3 is changed there\n-\t\texpect=$(printf '%s' \"$3\" | wc -c)\n-\t\t$test_do $PREREQ \"basic atom: $ref contents:size\" '\n-\t\t\ttype=$(git cat-file -t \"$ref\") &&\n-\t\t\tcase $type in\n-\t\t\ttag)\n-\t\t\t\t# We cannot use $3 as it expects sanitize_pgp to run\n-\t\t\t\tgit cat-file tag $ref >out &&\n-\t\t\t\texpect=$(tail -n +6 out | wc -c) &&\n-\t\t\t\trm -f out ;;\n-\t\t\ttree | blob)\n-\t\t\t\texpect=\"\" ;;\n-\t\t\tcommit)\n-\t\t\t\t: \"use the calculated expect\" ;;\n-\t\t\t*)\n-\t\t\t\tBUG \"unknown object type\" ;;\n-\t\t\tesac &&\n-\t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n-\t\t\techo $expect >expected &&\n-\t\t\tgit for-each-ref --format=\"%(contents:size)\" \"$ref\" >actual &&\n-\t\t\ttest_cmp expected actual\n-\t\t'\n-\tfi\n-}\n-\n-hexlen=$(test_oid hexsz)\n-\n-test_atom head refname refs/heads/main\n-test_atom head refname: refs/heads/main\n-test_atom head refname:short main\n-test_atom head refname:lstrip=1 heads/main\n-test_atom head refname:lstrip=2 main\n-test_atom head refname:lstrip=-1 main\n-test_atom head refname:lstrip=-2 heads/main\n-test_atom head refname:rstrip=1 refs/heads\n-test_atom head refname:rstrip=2 refs\n-test_atom head refname:rstrip=-1 refs\n-test_atom head refname:rstrip=-2 refs/heads\n-test_atom head refname:strip=1 heads/main\n-test_atom head refname:strip=2 main\n-test_atom head refname:strip=-1 main\n-test_atom head refname:strip=-2 heads/main\n-test_atom head upstream refs/remotes/origin/main\n-test_atom head upstream:short origin/main\n-test_atom head upstream:lstrip=2 origin/main\n-test_atom head upstream:lstrip=-2 origin/main\n-test_atom head upstream:rstrip=2 refs/remotes\n-test_atom head upstream:rstrip=-2 refs/remotes\n-test_atom head upstream:strip=2 origin/main\n-test_atom head upstream:strip=-2 origin/main\n-test_atom head push refs/remotes/myfork/main\n-test_atom head push:short myfork/main\n-test_atom head push:lstrip=1 remotes/myfork/main\n-test_atom head push:lstrip=-1 main\n-test_atom head push:rstrip=1 refs/remotes/myfork\n-test_atom head push:rstrip=-1 refs\n-test_atom head push:strip=1 remotes/myfork/main\n-test_atom head push:strip=-1 main\n-test_atom head objecttype commit\n-test_atom head objectsize $((131 + hexlen))\n-test_atom head objectsize:disk $(test_object_file_size refs/heads/main)\n-test_atom head deltabase $ZERO_OID\n-test_atom head objectname $(git rev-parse refs/heads/main)\n-test_atom head objectname:short $(git rev-parse --short refs/heads/main)\n-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n-test_atom head tree $(git rev-parse refs/heads/main^{tree})\n-test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})\n-test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})\n-test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})\n-test_atom head parent ''\n-test_atom head parent:short ''\n-test_atom head parent:short=1 ''\n-test_atom head parent:short=10 ''\n-test_atom head numparent 0\n-test_atom head object ''\n-test_atom head type ''\n-test_atom head raw \"$(git cat-file commit refs/heads/main)\n-\"\n-test_atom head '*objectname' ''\n-test_atom head '*objecttype' ''\n-test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n-test_atom head authorname 'A U Thor'\n-test_atom head authorname:mailmap 'A Thor'\n-test_atom head authoremail '<author@example.com>'\n-test_atom head authoremail:trim 'author@example.com'\n-test_atom head authoremail:localpart 'author'\n-test_atom head authoremail:trim,localpart 'author'\n-test_atom head authoremail:mailmap '<athor@example.com>'\n-test_atom head authoremail:mailmap,trim 'athor@example.com'\n-test_atom head authoremail:trim,mailmap 'athor@example.com'\n-test_atom head authoremail:mailmap,localpart 'athor'\n-test_atom head authoremail:localpart,mailmap 'athor'\n-test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'\n-test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n-test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n-test_atom head committername 'C O Mitter'\n-test_atom head committername:mailmap 'C Mitter'\n-test_atom head committeremail '<committer@example.com>'\n-test_atom head committeremail:trim 'committer@example.com'\n-test_atom head committeremail:localpart 'committer'\n-test_atom head committeremail:localpart,trim 'committer'\n-test_atom head committeremail:mailmap '<cmitter@example.com>'\n-test_atom head committeremail:mailmap,trim 'cmitter@example.com'\n-test_atom head committeremail:trim,mailmap 'cmitter@example.com'\n-test_atom head committeremail:mailmap,localpart 'cmitter'\n-test_atom head committeremail:localpart,mailmap 'cmitter'\n-test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'\n-test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n-test_atom head tag ''\n-test_atom head tagger ''\n-test_atom head taggername ''\n-test_atom head taggeremail ''\n-test_atom head taggeremail:trim ''\n-test_atom head taggeremail:localpart ''\n-test_atom head taggerdate ''\n-test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n-test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n-test_atom head subject 'Initial'\n-test_atom head subject:sanitize 'Initial'\n-test_atom head contents:subject 'Initial'\n-test_atom head body ''\n-test_atom head contents:body ''\n-test_atom head contents:signature ''\n-test_atom head contents 'Initial\n-'\n-test_atom head HEAD '*'\n-\n-test_atom tag refname refs/tags/testtag\n-test_atom tag refname:short testtag\n-test_atom tag upstream ''\n-test_atom tag push ''\n-test_atom tag objecttype tag\n-test_atom tag objectsize $((114 + hexlen))\n-test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)\n-test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)\n-test_atom tag deltabase $ZERO_OID\n-test_atom tag '*deltabase' $ZERO_OID\n-test_atom tag objectname $(git rev-parse refs/tags/testtag)\n-test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n-test_atom tag tree ''\n-test_atom tag tree:short ''\n-test_atom tag tree:short=1 ''\n-test_atom tag tree:short=10 ''\n-test_atom tag parent ''\n-test_atom tag parent:short ''\n-test_atom tag parent:short=1 ''\n-test_atom tag parent:short=10 ''\n-test_atom tag numparent ''\n-test_atom tag object $(git rev-parse refs/tags/testtag^0)\n-test_atom tag type 'commit'\n-test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})\n-test_atom tag '*objecttype' 'commit'\n-test_atom tag author ''\n-test_atom tag authorname ''\n-test_atom tag authorname:mailmap ''\n-test_atom tag authoremail ''\n-test_atom tag authoremail:trim ''\n-test_atom tag authoremail:localpart ''\n-test_atom tag authoremail:trim,localpart ''\n-test_atom tag authoremail:mailmap ''\n-test_atom tag authoremail:mailmap,trim ''\n-test_atom tag authoremail:trim,mailmap ''\n-test_atom tag authoremail:mailmap,localpart ''\n-test_atom tag authoremail:localpart,mailmap ''\n-test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''\n-test_atom tag authordate ''\n-test_atom tag committer ''\n-test_atom tag committername ''\n-test_atom tag committername:mailmap ''\n-test_atom tag committeremail ''\n-test_atom tag committeremail:trim ''\n-test_atom tag committeremail:localpart ''\n-test_atom tag committeremail:localpart,trim ''\n-test_atom tag committeremail:mailmap ''\n-test_atom tag committeremail:mailmap,trim ''\n-test_atom tag committeremail:trim,mailmap ''\n-test_atom tag committeremail:mailmap,localpart ''\n-test_atom tag committeremail:localpart,mailmap ''\n-test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''\n-test_atom tag committerdate ''\n-test_atom tag tag 'testtag'\n-test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n-test_atom tag taggername 'C O Mitter'\n-test_atom tag taggername:mailmap 'C Mitter'\n-test_atom tag taggeremail '<committer@example.com>'\n-test_atom tag taggeremail:trim 'committer@example.com'\n-test_atom tag taggeremail:localpart 'committer'\n-test_atom tag taggeremail:trim,localpart 'committer'\n-test_atom tag taggeremail:mailmap '<cmitter@example.com>'\n-test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'\n-test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'\n-test_atom tag taggeremail:mailmap,localpart 'cmitter'\n-test_atom tag taggeremail:localpart,mailmap 'cmitter'\n-test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'\n-test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n-test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n-test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n-test_atom tag subject 'Tagging at 1151968727'\n-test_atom tag subject:sanitize 'Tagging-at-1151968727'\n-test_atom tag contents:subject 'Tagging at 1151968727'\n-test_atom tag body ''\n-test_atom tag contents:body ''\n-test_atom tag contents:signature ''\n-test_atom tag contents 'Tagging at 1151968727\n-'\n-test_atom tag HEAD ' '\n-\n-test_expect_success 'basic atom: refs/tags/testtag *raw' '\n-\tgit cat-file commit refs/tags/testtag^{} >expected &&\n-\tgit for-each-ref --format=\"%(*raw)\" refs/tags/testtag >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_expect_success 'Check invalid atoms names are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'for-each-ref does not crash with -h' '\n+test_expect_success \"for-each-ref does not crash with -h\" '\n \ttest_expect_code 129 git for-each-ref -h >usage &&\n \ttest_grep \"[Uu]sage: git for-each-ref \" usage &&\n \ttest_expect_code 129 nongit git for-each-ref -h >usage &&\n \ttest_grep \"[Uu]sage: git for-each-ref \" usage\n '\n \n-test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n-\tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n-'\n-\n-test_expect_success 'Check valid format specifiers for date fields' '\n-\tgit for-each-ref --format=\"%(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:relative)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:short)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:local)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:iso8601)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:rfc2822)\" refs/heads\n-'\n-\n-test_expect_success 'Check invalid format specifiers are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n-'\n-\n-test_bad_atom () {\n-\tcase \"$1\" in\n-\thead) ref=refs/heads/main ;;\n-\t tag) ref=refs/tags/testtag ;;\n-\t sym) ref=refs/heads/sym ;;\n-\t   *) ref=$1 ;;\n-\tesac\n-\tformat=$2\n-\ttest_do=test_expect_${4:-success}\n-\n-\tprintf '%s\\n' \"$3\" >expect\n-\t$test_do $PREREQ \"err basic atom: $ref $format\" '\n-\t\ttest_must_fail git for-each-ref \\\n-\t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n-\t\ttest_cmp expect error\n-\t'\n-}\n-\n-test_bad_atom head 'authoremail:foo' \\\n-\t'fatal: unrecognized %(authoremail) argument: foo'\n-\n-test_bad_atom head 'authoremail:mailmap,trim,bar' \\\n-\t'fatal: unrecognized %(authoremail) argument: bar'\n-\n-test_bad_atom head 'authoremail:trim,' \\\n-\t'fatal: unrecognized %(authoremail) argument: '\n-\n-test_bad_atom head 'authoremail:mailmaptrim' \\\n-\t'fatal: unrecognized %(authoremail) argument: trim'\n-\n-test_bad_atom head 'committeremail: ' \\\n-\t'fatal: unrecognized %(committeremail) argument:  '\n-\n-test_bad_atom head 'committeremail: trim,foo' \\\n-\t'fatal: unrecognized %(committeremail) argument:  trim,foo'\n-\n-test_bad_atom head 'committeremail:mailmap,localpart ' \\\n-\t'fatal: unrecognized %(committeremail) argument:  '\n-\n-test_bad_atom head 'committeremail:trim_localpart' \\\n-\t'fatal: unrecognized %(committeremail) argument: _localpart'\n-\n-test_bad_atom head 'committeremail:localpart,,,trim' \\\n-\t'fatal: unrecognized %(committeremail) argument: ,,trim'\n-\n-test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \\\n-\t'fatal: unrecognized %(taggeremail) argument:  foo '\n-\n-test_bad_atom tag 'taggeremail:trim,localpart,' \\\n-\t'fatal: unrecognized %(taggeremail) argument: '\n-\n-test_bad_atom tag 'taggeremail:mailmap;localpart trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument: ;localpart trim'\n-\n-test_bad_atom tag 'taggeremail:localpart trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument:  trim'\n-\n-test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'\n-\n-test_date () {\n-\tf=$1 &&\n-\tcommitter_date=$2 &&\n-\tauthor_date=$3 &&\n-\ttagger_date=$4 &&\n-\tcat >expected <<-EOF &&\n-\t'refs/heads/main' '$committer_date' '$author_date'\n-\t'refs/tags/testtag' '$tagger_date'\n-\tEOF\n-\t(\n-\t\tgit for-each-ref --shell \\\n-\t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n-\t\t\trefs/heads &&\n-\t\tgit for-each-ref --shell \\\n-\t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n-\t\t\trefs/tags\n-\t) >actual &&\n-\ttest_cmp expected actual\n-}\n-\n-test_expect_success 'Check unformatted date fields output' '\n-\ttest_date \"\" \\\n-\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n-'\n-\n-test_expect_success 'Check format \"default\" formatted date fields output' '\n-\ttest_date default \\\n-\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n-'\n-\n-test_expect_success 'Check format \"default-local\" date fields output' '\n-\ttest_date default-local \"Mon Jul 3 23:18:43 2006\" \"Mon Jul 3 23:18:44 2006\" \"Mon Jul 3 23:18:45 2006\"\n-'\n-\n-# Don't know how to do relative check because I can't know when this script\n-# is going to be run and can't fake the current time to git, and hence can't\n-# provide expected output.  Instead, I'll just make sure that \"relative\"\n-# doesn't exit in error\n-test_expect_success 'Check format \"relative\" date fields output' '\n-\tf=relative &&\n-\t(git for-each-ref --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n-\tgit for-each-ref --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n-'\n-\n-# We just check that this is the same as \"relative\" for now.\n-test_expect_success 'Check format \"relative-local\" date fields output' '\n-\ttest_date relative-local \\\n-\t\t\"$(git for-each-ref --format=\"%(committerdate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(authordate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(taggerdate:relative)\" refs/tags)\"\n-'\n-\n-test_expect_success 'Check format \"short\" date fields output' '\n-\ttest_date short 2006-07-04 2006-07-04 2006-07-04\n-'\n-\n-test_expect_success 'Check format \"short-local\" date fields output' '\n-\ttest_date short-local 2006-07-03 2006-07-03 2006-07-03\n-'\n-\n-test_expect_success 'Check format \"local\" date fields output' '\n-\ttest_date local \\\n-\t\t\"Mon Jul 3 23:18:43 2006\" \\\n-\t\t\"Mon Jul 3 23:18:44 2006\" \\\n-\t\t\"Mon Jul 3 23:18:45 2006\"\n-'\n-\n-test_expect_success 'Check format \"iso8601\" date fields output' '\n-\ttest_date iso8601 \\\n-\t\t\"2006-07-04 01:18:43 +0200\" \\\n-\t\t\"2006-07-04 01:18:44 +0200\" \\\n-\t\t\"2006-07-04 01:18:45 +0200\"\n-'\n-\n-test_expect_success 'Check format \"iso8601-local\" date fields output' '\n-\ttest_date iso8601-local \"2006-07-03 23:18:43 +0000\" \"2006-07-03 23:18:44 +0000\" \"2006-07-03 23:18:45 +0000\"\n-'\n-\n-test_expect_success 'Check format \"rfc2822\" date fields output' '\n-\ttest_date rfc2822 \\\n-\t\t\"Tue, 4 Jul 2006 01:18:43 +0200\" \\\n-\t\t\"Tue, 4 Jul 2006 01:18:44 +0200\" \\\n-\t\t\"Tue, 4 Jul 2006 01:18:45 +0200\"\n-'\n-\n-test_expect_success 'Check format \"rfc2822-local\" date fields output' '\n-\ttest_date rfc2822-local \"Mon, 3 Jul 2006 23:18:43 +0000\" \"Mon, 3 Jul 2006 23:18:44 +0000\" \"Mon, 3 Jul 2006 23:18:45 +0000\"\n-'\n-\n-test_expect_success 'Check format \"raw\" date fields output' '\n-\ttest_date raw \"1151968723 +0200\" \"1151968724 +0200\" \"1151968725 +0200\"\n-'\n-\n-test_expect_success 'Check format \"raw-local\" date fields output' '\n-\ttest_date raw-local \"1151968723 +0000\" \"1151968724 +0000\" \"1151968725 +0000\"\n-'\n-\n-test_expect_success 'Check format of strftime date fields' '\n-\techo \"my date is 2006-07-04\" >expected &&\n-\tgit for-each-ref \\\n-\t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n-\t  refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Check format of strftime-local date fields' '\n-\techo \"my date is 2006-07-03\" >expected &&\n-\tgit for-each-ref \\\n-\t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n-\t  refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'exercise strftime with odd fields' '\n-\techo >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:)\" refs/heads >actual &&\n-\ttest_cmp expected actual &&\n-\tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n-\techo $long >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/heads/main\n-refs/remotes/origin/main\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'Verify ascending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=refname >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/remotes/origin/main\n-refs/heads/main\n-EOF\n-\n-test_expect_success 'Verify descending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=-refname >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Give help even with invalid sort atoms' '\n-\ttest_expect_code 129 git for-each-ref --sort=bogus -h >actual 2>&1 &&\n-\tgrep \"^usage: git for-each-ref\" actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/tags/testtag-2\n-EOF\n-\n-test_expect_success 'exercise patterns with prefixes' '\n-\tgit tag testtag-2 &&\n-\ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/tags/testtag-2\n-EOF\n-\n-test_expect_success 'exercise glob patterns with prefixes' '\n-\tgit tag testtag-2 &&\n-\ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/bar\n-refs/tags/baz\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'exercise patterns with prefix exclusions' '\n-\tfor tag in foo/one foo/two foo/three bar baz\n-\tdo\n-\t\tgit tag \"$tag\" || return 1\n-\tdone &&\n-\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/bar\n-refs/tags/baz\n-refs/tags/foo/one\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'exercise patterns with pattern exclusions' '\n-\tfor tag in foo/one foo/two foo/three bar baz\n-\tdo\n-\t\tgit tag \"$tag\" || return 1\n-\tdone &&\n-\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-'refs/heads/main'\n-'refs/remotes/origin/main'\n-'refs/tags/testtag'\n-EOF\n-\n-test_expect_success 'Quoting style: shell' '\n-\tgit for-each-ref --shell --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Quoting style: perl' '\n-\tgit for-each-ref --perl --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Quoting style: python' '\n-\tgit for-each-ref --python --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-\"refs/heads/main\"\n-\"refs/remotes/origin/main\"\n-\"refs/tags/testtag\"\n-EOF\n-\n-test_expect_success 'Quoting style: tcl' '\n-\tgit for-each-ref --tcl --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n-\ttest_expect_success \"more than one quoting style: $i\" \"\n-\t\ttest_must_fail git for-each-ref $i 2>err &&\n-\t\tgrep '^error: more than one quoting style' err\n-\t\"\n-done\n-\n-test_expect_success 'setup for upstream:track[short]' '\n-\ttest_commit two\n-'\n-\n-test_atom head upstream:track '[ahead 1]'\n-test_atom head upstream:trackshort '>'\n-test_atom head upstream:track,nobracket 'ahead 1'\n-test_atom head upstream:nobracket,track 'ahead 1'\n-\n-test_expect_success 'setup for push:track[short]' '\n-\ttest_commit third &&\n-\tgit update-ref refs/remotes/myfork/main main &&\n-\tgit reset main~1\n-'\n-\n-test_atom head push:track '[behind 1]'\n-test_atom head push:trackshort '<'\n-\n-test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:track)\" 2>/dev/null &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:trackshort)\" 2>/dev/null\n-'\n-\n-test_expect_success 'Check that :track[short] works when upstream is invalid' '\n-\tcat >expected <<-\\EOF &&\n-\t[gone]\n-\n-\tEOF\n-\ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n-\tgit config branch.main.merge refs/heads/does-not-exist &&\n-\tgit for-each-ref \\\n-\t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n-\t\trefs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Check for invalid refname format' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n-'\n-\n-test_expect_success 'set up color tests' '\n-\tcat >expected.color <<-EOF &&\n-\t$(git rev-parse --short refs/heads/main) <GREEN>main<RESET>\n-\t$(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>\n-\t$(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>\n-\t$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>\n-\t$(git rev-parse --short refs/tags/third) <GREEN>third<RESET>\n-\t$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>\n-\tEOF\n-\tsed \"s/<[^>]*>//g\" <expected.color >expected.bare &&\n-\tcolor_format=\"%(objectname:short) %(color:green)%(refname:short)\"\n-'\n-\n-test_expect_success TTY '%(color) shows color with a tty' '\n-\ttest_terminal git for-each-ref --format=\"$color_format\" >actual.raw &&\n-\ttest_decode_color <actual.raw >actual &&\n-\ttest_cmp expected.color actual\n-'\n-\n-test_expect_success '%(color) does not show color without tty' '\n-\tTERM=vt100 git for-each-ref --format=\"$color_format\" >actual &&\n-\ttest_cmp expected.bare actual\n-'\n-\n-test_expect_success '--color can override tty check' '\n-\tgit for-each-ref --color --format=\"$color_format\" >actual.raw &&\n-\ttest_decode_color <actual.raw >actual &&\n-\ttest_cmp expected.color actual\n-'\n-\n-test_expect_success 'color.ui=always does not override tty check' '\n-\tgit -c color.ui=always for-each-ref --format=\"$color_format\" >actual &&\n-\ttest_cmp expected.bare actual\n-'\n-\n-test_expect_success 'setup for describe atom tests' '\n-\tgit init -b master describe-repo &&\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\ttest_commit --no-tag one &&\n-\t\tgit tag tagone &&\n-\n-\t\ttest_commit --no-tag two &&\n-\t\tgit tag -a -m \"tag two\" tagtwo\n-\t)\n-'\n-\n-test_expect_success 'describe atom vs git describe' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\tgit for-each-ref --format=\"%(objectname)\" \\\n-\t\t\trefs/tags/ >obj &&\n-\t\twhile read hash\n-\t\tdo\n-\t\t\tif desc=$(git describe $hash)\n-\t\t\tthen\n-\t\t\t\t: >expect-contains-good\n-\t\t\telse\n-\t\t\t\t: >expect-contains-bad\n-\t\t\tfi &&\n-\t\t\techo \"$hash $desc\" || return 1\n-\t\tdone <obj >expect &&\n-\t\ttest_path_exists expect-contains-good &&\n-\t\ttest_path_exists expect-contains-bad &&\n-\n-\t\tgit for-each-ref --format=\"%(objectname) %(describe)\" \\\n-\t\t\trefs/tags/ >actual 2>err &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest_must_be_empty err\n-\t)\n-'\n-\n-test_expect_success 'describe:tags vs describe --tags' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit describe --tags >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:tags)\" \\\n-\t\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\t# Case 1: We have commits between HEAD and the most\n-\t\t#\t  recent tag reachable from it\n-\t\ttest_commit --no-tag file &&\n-\t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual &&\n-\n-\t\t# Make sure the hash used is at least 14 digits long\n-\t\tsed -e \"s/^.*-g\\([0-9a-f]*\\)$/\\1/\" <actual >hexpart &&\n-\t\ttest 15 -le $(wc -c <hexpart) &&\n-\n-\t\t# Case 2: We have a tag at HEAD, describe directly gives\n-\t\t#\t  the name of the tag\n-\t\tgit tag -a -m tagged tagname &&\n-\t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest tagname = $(cat actual)\n-\t)\n-'\n-\n-test_expect_success 'describe:match=... vs describe --match ...' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit tag -a -m \"tag foo\" tag-foo &&\n-\t\tgit describe --match \"*-foo\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:match=\"*-foo\")\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit tag -a -m \"tag bar\" tag-bar &&\n-\t\tgit describe --exclude \"*-bar\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:exclude=\"*-bar\")\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'deref with describe atom' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tcat >expect <<-\\EOF &&\n-\n-\t\ttagname\n-\t\ttagname\n-\t\ttagname\n-\n-\t\ttagtwo\n-\t\tEOF\n-\t\tgit for-each-ref --format=\"%(*describe)\" >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'err on bad describe atom arg' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\t# The bad arg is the only arg passed to describe atom\n-\t\tcat >expect <<-\\EOF &&\n-\t\tfatal: unrecognized %(describe) argument: baz\n-\t\tEOF\n-\t\ttest_must_fail git for-each-ref --format=\"%(describe:baz)\" \\\n-\t\t\trefs/heads/master 2>actual &&\n-\t\ttest_cmp expect actual &&\n-\n-\t\t# The bad arg is in the middle of the option string\n-\t\t# passed to the describe atom\n-\t\tcat >expect <<-\\EOF &&\n-\t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n-\t\tEOF\n-\t\ttest_must_fail git for-each-ref \\\n-\t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n-\t\t\tref/heads/master 2>actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-cat >expected <<\\EOF\n-heads/main\n-tags/main\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs (strict)' '\n-\tgit config --bool core.warnambiguousrefs true &&\n-\tgit checkout -b newtag &&\n-\techo \"Using $datestamp\" > one &&\n-\tgit add one &&\n-\tgit commit -m \"Branch\" &&\n-\tsetdate_and_increment &&\n-\tgit tag -m \"Tagging at $datestamp\" main &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-heads/main\n-main\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs (loose)' '\n-\tgit config --bool core.warnambiguousrefs false &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-heads/ambiguous\n-ambiguous\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n-\tgit checkout main &&\n-\tgit tag ambiguous testtag^0 &&\n-\tgit branch ambiguous testtag^0 &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'create tag without tagger' '\n-\tgit tag -a -m \"Broken tag\" taggerless &&\n-\tgit tag -f taggerless $(git cat-file tag taggerless |\n-\t\tsed -e \"/^tagger /d\" |\n-\t\tgit hash-object --literally --stdin -w -t tag)\n-'\n-\n-test_atom refs/tags/taggerless type 'commit'\n-test_atom refs/tags/taggerless tag 'taggerless'\n-test_atom refs/tags/taggerless tagger ''\n-test_atom refs/tags/taggerless taggername ''\n-test_atom refs/tags/taggerless taggeremail ''\n-test_atom refs/tags/taggerless taggeremail:trim ''\n-test_atom refs/tags/taggerless taggeremail:localpart ''\n-test_atom refs/tags/taggerless taggerdate ''\n-test_atom refs/tags/taggerless committer ''\n-test_atom refs/tags/taggerless committername ''\n-test_atom refs/tags/taggerless committeremail ''\n-test_atom refs/tags/taggerless committeremail:trim ''\n-test_atom refs/tags/taggerless committeremail:localpart ''\n-test_atom refs/tags/taggerless committerdate ''\n-test_atom refs/tags/taggerless subject 'Broken tag'\n-\n-test_expect_success 'an unusual tag with an incomplete line' '\n-\n-\tgit tag -m \"bogo\" bogo &&\n-\tbogo=$(git cat-file tag bogo) &&\n-\tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n-\tgit tag -f bogo \"$bogo\" &&\n-\tgit for-each-ref --format \"%(body)\" refs/tags/bogo\n-\n-'\n-\n-test_expect_success 'create tag with subject and body content' '\n-\tcat >>msg <<-\\EOF &&\n-\t\tthe subject line\n-\n-\t\tfirst body line\n-\t\tsecond body line\n-\tEOF\n-\tgit tag -F msg subject-body\n-'\n-test_atom refs/tags/subject-body subject 'the subject line'\n-test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n-test_atom refs/tags/subject-body body 'first body line\n-second body line\n-'\n-test_atom refs/tags/subject-body contents 'the subject line\n-\n-first body line\n-second body line\n-'\n-\n-test_expect_success 'create tag with multiline subject' '\n-\tcat >msg <<-\\EOF &&\n-\t\tfirst subject line\n-\t\tsecond subject line\n-\n-\t\tfirst body line\n-\t\tsecond body line\n-\tEOF\n-\tgit tag -F msg multiline\n-'\n-test_atom refs/tags/multiline subject 'first subject line second subject line'\n-test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n-test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n-test_atom refs/tags/multiline body 'first body line\n-second body line\n-'\n-test_atom refs/tags/multiline contents:body 'first body line\n-second body line\n-'\n-test_atom refs/tags/multiline contents:signature ''\n-test_atom refs/tags/multiline contents 'first subject line\n-second subject line\n-\n-first body line\n-second body line\n-'\n-\n-test_expect_success GPG 'create signed tags' '\n-\tgit tag -s -m \"\" signed-empty &&\n-\tgit tag -s -m \"subject line\" signed-short &&\n-\tcat >msg <<-\\EOF &&\n-\tsubject line\n-\n-\tbody contents\n-\tEOF\n-\tgit tag -s -F msg signed-long\n-'\n-\n-sig='-----BEGIN PGP SIGNATURE-----\n------END PGP SIGNATURE-----\n-'\n-\n-PREREQ=GPG\n-test_atom refs/tags/signed-empty subject ''\n-test_atom refs/tags/signed-empty subject:sanitize ''\n-test_atom refs/tags/signed-empty contents:subject ''\n-test_atom refs/tags/signed-empty body \"$sig\"\n-test_atom refs/tags/signed-empty contents:body ''\n-test_atom refs/tags/signed-empty contents:signature \"$sig\"\n-test_atom refs/tags/signed-empty contents \"$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n-\tgit cat-file tag refs/tags/signed-empty >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_atom refs/tags/signed-short subject 'subject line'\n-test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n-test_atom refs/tags/signed-short contents:subject 'subject line'\n-test_atom refs/tags/signed-short body \"$sig\"\n-test_atom refs/tags/signed-short contents:body ''\n-test_atom refs/tags/signed-short contents:signature \"$sig\"\n-test_atom refs/tags/signed-short contents \"subject line\n-$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n-\tgit cat-file tag refs/tags/signed-short >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-short >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_atom refs/tags/signed-long subject 'subject line'\n-test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n-test_atom refs/tags/signed-long contents:subject 'subject line'\n-test_atom refs/tags/signed-long body \"body contents\n-$sig\"\n-test_atom refs/tags/signed-long contents:body 'body contents\n-'\n-test_atom refs/tags/signed-long contents:signature \"$sig\"\n-test_atom refs/tags/signed-long contents \"subject line\n-\n-body contents\n-$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n-\tgit cat-file tag refs/tags/signed-long >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-long >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_expect_success 'set up refs pointing to tree and blob' '\n-\tgit update-ref refs/mytrees/first refs/heads/main^{tree} &&\n-\tgit update-ref refs/myblobs/first refs/heads/main:one\n-'\n-\n-test_atom refs/mytrees/first subject \"\"\n-test_atom refs/mytrees/first contents:subject \"\"\n-test_atom refs/mytrees/first body \"\"\n-test_atom refs/mytrees/first contents:body \"\"\n-test_atom refs/mytrees/first contents:signature \"\"\n-test_atom refs/mytrees/first contents \"\"\n-\n-test_expect_success 'basic atom: refs/mytrees/first raw' '\n-\tgit cat-file tree refs/mytrees/first >expected &&\n-\techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual &&\n-\tgit cat-file -s refs/mytrees/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_atom refs/myblobs/first subject \"\"\n-test_atom refs/myblobs/first contents:subject \"\"\n-test_atom refs/myblobs/first body \"\"\n-test_atom refs/myblobs/first contents:body \"\"\n-test_atom refs/myblobs/first contents:signature \"\"\n-test_atom refs/myblobs/first contents \"\"\n-\n-test_expect_success 'basic atom: refs/myblobs/first raw' '\n-\tgit cat-file blob refs/myblobs/first >expected &&\n-\techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/myblobs/first >actual &&\n-\ttest_cmp expected actual &&\n-\tgit cat-file -s refs/myblobs/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'set up refs pointing to binary blob' '\n-\tprintf \"a\\0b\\0c\" >blob1 &&\n-\tprintf \"a\\0c\\0b\" >blob2 &&\n-\tprintf \"\\0a\\0b\\0c\" >blob3 &&\n-\tprintf \"abc\" >blob4 &&\n-\tprintf \"\\0 \\0 \\0 \" >blob5 &&\n-\tprintf \"\\0 \\0a\\0 \" >blob6 &&\n-\tprintf \"  \" >blob7 &&\n-\t>blob8 &&\n-\tobj=$(git hash-object -w blob1) &&\n-\tgit update-ref refs/myblobs/blob1 \"$obj\" &&\n-\tobj=$(git hash-object -w blob2) &&\n-\tgit update-ref refs/myblobs/blob2 \"$obj\" &&\n-\tobj=$(git hash-object -w blob3) &&\n-\tgit update-ref refs/myblobs/blob3 \"$obj\" &&\n-\tobj=$(git hash-object -w blob4) &&\n-\tgit update-ref refs/myblobs/blob4 \"$obj\" &&\n-\tobj=$(git hash-object -w blob5) &&\n-\tgit update-ref refs/myblobs/blob5 \"$obj\" &&\n-\tobj=$(git hash-object -w blob6) &&\n-\tgit update-ref refs/myblobs/blob6 \"$obj\" &&\n-\tobj=$(git hash-object -w blob7) &&\n-\tgit update-ref refs/myblobs/blob7 \"$obj\" &&\n-\tobj=$(git hash-object -w blob8) &&\n-\tgit update-ref refs/myblobs/blob8 \"$obj\"\n-'\n-\n-test_expect_success 'Verify sorts with raw' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob8\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/blob3\n-\trefs/myblobs/blob7\n-\trefs/mytrees/first\n-\trefs/myblobs/first\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob4\n-\trefs/heads/main\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw \\\n-\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Verify sorts with raw:size' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob8\n-\trefs/myblobs/blob7\n-\trefs/myblobs/blob4\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob3\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/first\n-\trefs/mytrees/first\n-\trefs/heads/main\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw:size \\\n-\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'validate raw atom with %(if:equals)' '\n-\tcat >expected <<-EOF &&\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\trefs/myblobs/blob4\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tEOF\n-\tgit for-each-ref --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n-\t\trefs/myblobs/ refs/heads/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'validate raw atom with %(if:notequals)' '\n-\tcat >expected <<-EOF &&\n-\trefs/heads/ambiguous\n-\trefs/heads/main\n-\trefs/heads/newtag\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob3\n-\tequals\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/blob7\n-\trefs/myblobs/blob8\n-\trefs/myblobs/first\n-\tEOF\n-\tgit for-each-ref --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n-\t\trefs/myblobs/ refs/heads/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'empty raw refs with %(if)' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob1 not empty\n-\trefs/myblobs/blob2 not empty\n-\trefs/myblobs/blob3 not empty\n-\trefs/myblobs/blob4 not empty\n-\trefs/myblobs/blob5 not empty\n-\trefs/myblobs/blob6 not empty\n-\trefs/myblobs/blob7 empty\n-\trefs/myblobs/blob8 empty\n-\trefs/myblobs/first not empty\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n-\t\trefs/myblobs/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '%(raw) with --python must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --python\n-'\n-\n-test_expect_success '%(raw) with --tcl must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --tcl\n-'\n-\n-test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n-\tcmp blob1 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n-\tcmp blob3 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n-\tcmp blob8 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n-\tcmp one actual &&\n-\tgit cat-file tree refs/mytrees/first > expected &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n-\tcmp expected actual\n-'\n-\n-test_expect_success '%(raw) with --shell must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --shell\n-'\n-\n-test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --sort=raw --shell\n-'\n-\n-test_expect_success '%(raw:size) with --shell' '\n-\tgit for-each-ref --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n-\tgit for-each-ref --format=\"%(raw:size)\" --shell >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --format compare with cat-file --batch' '\n-\tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n-\tgit for-each-ref --format=\"%(objectname) %(objecttype) %(objectsize)\n-%(raw)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'verify sorts with contents:size' '\n-\tcat >expect <<-\\EOF &&\n-\trefs/heads/main\n-\trefs/heads/newtag\n-\trefs/heads/ambiguous\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\t--sort=contents:size refs/heads/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'set up multiple-sort tags' '\n-\tfor when in 100000 200000\n-\tdo\n-\t\tfor email in user1 user2\n-\t\tdo\n-\t\t\tfor ref in ref1 ref2\n-\t\t\tdo\n-\t\t\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n-\t\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n-\t\t\t\tgit tag -m \"tag $ref-$when-$email\" \\\n-\t\t\t\tmulti-$ref-$when-$email || return 1\n-\t\t\tdone\n-\t\tdone\n-\tdone\n-'\n-\n-test_expect_success 'Verify sort with multiple keys' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=-refname \\\n-\t\t--sort=taggeremail \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'equivalent sorts fall back on refname' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '--no-sort cancels the previous sort keys' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=-refname \\\n-\t\t--sort=taggeremail \\\n-\t\t--no-sort \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n-\tcat >expected <<-\\EOF &&\n-\trefs/tags/multi-ref1-100000-user1\n-\trefs/tags/multi-ref1-100000-user2\n-\trefs/tags/multi-ref1-200000-user1\n-\trefs/tags/multi-ref1-200000-user2\n-\trefs/tags/multi-ref2-100000-user1\n-\trefs/tags/multi-ref2-100000-user2\n-\trefs/tags/multi-ref2-200000-user1\n-\trefs/tags/multi-ref2-200000-user2\n-\tEOF\n-\n-\t# Sort the results with `sort` for a consistent comparison against\n-\t# expected\n-\tgit for-each-ref \\\n-\t\t--format=\"%(refname)\" \\\n-\t\t--no-sort \\\n-\t\t\"refs/tags/multi-*\" | sort >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'set up custom date sorting' '\n-\t# Dates:\n-\t# - Wed Feb 07 2024 21:34:20 +0000\n-\t# - Tue Dec 14 1999 00:05:22 +0000\n-\t# - Fri Jun 04 2021 11:26:51 +0000\n-\t# - Mon Jan 22 2007 16:44:01 GMT+0000\n-\ti=1 &&\n-\tfor when in 1707341660 945129922 1622806011 1169484241\n-\tdo\n-\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n-\t\tGIT_COMMITTER_EMAIL=\"user@example.com\" \\\n-\t\tgit tag -m \"tag $when\" custom-dates-$i &&\n-\t\ti=$(($i+1)) || return 1\n-\tdone\n-'\n-\n-test_expect_success 'sort by date defaults to full timestamp' '\n-\tcat >expected <<-\\EOF &&\n-\t945129922 refs/tags/custom-dates-2\n-\t1169484241 refs/tags/custom-dates-4\n-\t1622806011 refs/tags/custom-dates-3\n-\t1707341660 refs/tags/custom-dates-1\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(creatordate:unix) %(refname)\" \\\n-\t\t--sort=creatordate \\\n-\t\t\"refs/tags/custom-dates-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'sort by custom date format' '\n-\tcat >expected <<-\\EOF &&\n-\t00:05:22 refs/tags/custom-dates-2\n-\t11:26:51 refs/tags/custom-dates-3\n-\t16:44:01 refs/tags/custom-dates-4\n-\t21:34:20 refs/tags/custom-dates-1\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n-\t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n-\t\t\"refs/tags/custom-dates-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n-\ttest_when_finished \"git checkout main\" &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n-\tsed -e \"s/^\\* /  /\" actual >expect &&\n-\tgit checkout --orphan orphaned-branch &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-cat >trailers <<EOF\n-Reviewed-by: A U Thor <author@example.com>\n-Signed-off-by: A U Thor <author@example.com>\n-[ v2 updated patch description ]\n-Acked-by: A U Thor\n-  <author@example.com>\n-EOF\n-\n-unfold () {\n-\tperl -0pe 's/\\n\\s+/ /g'\n-}\n-\n-test_expect_success 'set up trailers for next test' '\n-\techo \"Some contents\" > two &&\n-\tgit add two &&\n-\tgit commit -F - <<-EOF\n-\ttrailers: this commit message has trailers\n-\n-\tSome message contents\n-\n-\t$(cat trailers)\n-\tEOF\n-'\n-\n-test_trailer_option () {\n-\tif test \"$#\" -eq 3\n-\tthen\n-\t\tprereq=\"$1\"\n-\t\tshift\n-\tfi &&\n-\ttitle=$1 option=$2\n-\tcat >expect\n-\ttest_expect_success $prereq \"$title\" '\n-\t\tgit for-each-ref --format=\"%($option)\" refs/heads/main >actual &&\n-\t\ttest_cmp expect actual &&\n-\t\tgit for-each-ref --format=\"%(contents:$option)\" refs/heads/main >actual &&\n-\t\ttest_cmp expect actual\n-\t'\n-}\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \\\n-\t'trailers:unfold' <<-EOF\n-\t$(unfold <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only) shows only \"key: value\" trailers' \\\n-\t'trailers:only' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=no,only=true) shows only \"key: value\" trailers' \\\n-\t'trailers:only=no,only=true' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=yes) shows only \"key: value\" trailers' \\\n-\t'trailers:only=yes' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=no) shows all trailers' \\\n-\t'trailers:only=no' <<-EOF\n-\t$(cat trailers)\n-\n-\tEOF\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \\\n-\t'trailers:only,unfold' <<-EOF\n-\t$(grep -v patch.description <trailers | unfold)\n-\n-\tEOF\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \\\n-\t'trailers:unfold,only' <<-EOF\n-\t$(grep -v patch.description <trailers | unfold)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) shows that trailer' \\\n-\t'trailers:key=Signed-off-by' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) is case insensitive' \\\n-\t'trailers:key=SiGned-oFf-bY' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo:) trailing colon also works' \\\n-\t'trailers:key=Signed-off-by:' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) multiple keys' \\\n-\t'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF\n-\tReviewed-by: A U Thor <author@example.com>\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=nonexistent) becomes empty' \\\n-\t'trailers:key=Shined-off-by:' <<-EOF\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \\\n-\t'trailers:key=Acked-by' <<-EOF\n-\t$(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \\\n-\t'trailers:key=Signed-Off-by,unfold' <<-EOF\n-\t$(unfold <trailers | grep Signed-off-by)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \\\n-\t'trailers:key=Signed-off-by,only=no' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\t$(grep patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \\\n-\t'trailers:key=Signed-off-by,valueonly' <<-EOF\n-\tA U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:separator) changes separator' \\\n-\t'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>\n-\tEOF\n-\n-test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \\\n-\t'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by,A U Thor <author@example.com>\n-\tSigned-off-by,A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \\\n-\t'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>\n-\tEOF\n-\n-test_expect_success 'multiple %(trailers) use their own options' '\n-\tgit tag -F - tag-with-trailers <<-\\EOF &&\n-\tbody\n-\n-\tone: foo\n-\tone: bar\n-\ttwo: baz\n-\ttwo: qux\n-\tEOF\n-\tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n-\tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n-\tgit for-each-ref --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n-\tcat >expect <<-\\EOF &&\n-\toneWfooXoneWbar\n-\ttwoYbazZtwoYqux\n-\tEOF\n-\ttest_cmp expect actual\n-'\n-\n-test_failing_trailer_option () {\n-\ttitle=$1 option=$2\n-\tcat >expect\n-\ttest_expect_success \"$title\" '\n-\t\t# error message cannot be checked under i18n\n-\t\ttest_must_fail git for-each-ref --format=\"%($option)\" refs/heads/main 2>actual &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest_must_fail git for-each-ref --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n-\t\ttest_cmp expect actual\n-\t'\n-}\n-\n-test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \\\n-\t'trailers:unsupported' <<-\\EOF\n-\tfatal: unknown %(trailers) argument: unsupported\n-\tEOF\n-\n-test_failing_trailer_option '%(trailers:key) without value is error' \\\n-\t'trailers:key' <<-\\EOF\n-\tfatal: expected %(trailers:key=<value>)\n-\tEOF\n-\n-test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '\n-\tcat >expect <<-EOF &&\n-\tfatal: unrecognized %(contents) argument: trailersonly\n-\tEOF\n-\ttest_must_fail git for-each-ref --format=\"%(contents:trailersonly)\" 2>actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'basic atom: head contents:trailers' '\n-\tgit for-each-ref --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\t# git for-each-ref ends with a blank line\n-\tcat >expect <<-EOF &&\n-\t$(cat trailers)\n-\n-\tEOF\n-\ttest_cmp expect actual.clean\n-'\n-\n-test_expect_success 'basic atom: rest must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(rest)\" refs/heads/main\n-'\n-\n-test_expect_success 'HEAD atom does not take arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(HEAD:foo)\" 2>err &&\n-\techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'subject atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(subject:foo)\" 2>err &&\n-\techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'refname atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:foo)\" 2>err &&\n-\techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'trailer parsing not fooled by --- line' '\n-\tgit commit --allow-empty -F - <<-\\EOF &&\n-\tthis is the subject\n-\n-\tThis is the body. The message has a \"---\" line which would confuse a\n-\tmessage+patch parser. But here we know we have only a commit message,\n-\tso we get it right.\n-\n-\ttrailer: wrong\n-\t---\n-\tThis is more body.\n-\n-\ttrailer: right\n-\tEOF\n-\n-\t{\n-\t\techo \"trailer: right\" &&\n-\t\techo\n-\t} >expect &&\n-\tgit for-each-ref --format=\"%(trailers)\" refs/heads/main >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'Add symbolic ref for the following tests' '\n-\tgit symbolic-ref refs/heads/sym refs/heads/main\n-'\n-\n-cat >expected <<EOF\n-refs/heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref) atom' '\n-\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:short) atom' '\n-\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-main\n-heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual &&\n-\n-\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-refs\n-refs/heads\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success ':remotename and :remoteref' '\n-\tgit init remote-tests &&\n-\t(\n-\t\tcd remote-tests &&\n-\t\ttest_commit initial &&\n-\t\tgit branch -M main &&\n-\t\tgit remote add from fifth.coffee:blub &&\n-\t\tgit config branch.main.remote from &&\n-\t\tgit config branch.main.merge refs/heads/stable &&\n-\t\tgit remote add to southridge.audio:repo &&\n-\t\tgit config remote.to.push \"refs/heads/*:refs/heads/pushed/*\" &&\n-\t\tgit config branch.main.pushRemote to &&\n-\t\tfor pair in \"%(upstream)=refs/remotes/from/stable\" \\\n-\t\t\t\"%(upstream:remotename)=from\" \\\n-\t\t\t\"%(upstream:remoteref)=refs/heads/stable\" \\\n-\t\t\t\"%(push)=refs/remotes/to/pushed/main\" \\\n-\t\t\t\"%(push:remotename)=to\" \\\n-\t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n-\t\tdo\n-\t\t\techo \"${pair#*=}\" >expect &&\n-\t\t\tgit for-each-ref --format=\"${pair%=*}\" \\\n-\t\t\t\trefs/heads/main >actual &&\n-\t\t\ttest_cmp expect actual || exit 1\n-\t\tdone &&\n-\t\tgit branch push-simple &&\n-\t\tgit config branch.push-simple.pushRemote from &&\n-\t\tactual=\"$(git for-each-ref \\\n-\t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n-\t\t\trefs/heads/push-simple)\" &&\n-\t\ttest from, = \"$actual\"\n-\t)\n-'\n-\n-test_expect_success 'for-each-ref --ignore-case ignores case' '\n-\tgit for-each-ref --format=\"%(refname)\" refs/heads/MAIN >actual &&\n-\ttest_must_be_empty actual &&\n-\n-\techo refs/heads/main >expect &&\n-\tgit for-each-ref --format=\"%(refname)\" --ignore-case \\\n-\t\trefs/heads/MAIN >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --omit-empty works' '\n-\tgit for-each-ref --format=\"%(refname)\" >actual &&\n-\ttest_line_count -gt 1 actual &&\n-\tgit for-each-ref --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n-\techo refs/heads/main >expect &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n-\t# name refs numerically to avoid case-insensitive filesystem conflicts\n-\tnr=0 &&\n-\tfor email in a A b B\n-\tdo\n-\t\tfor subject in a A b B\n-\t\tdo\n-\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n-\t\t\tgit tag -m \"tag $subject\" icase-$(printf %02d $nr) &&\n-\t\t\tnr=$((nr+1))||\n-\t\t\treturn 1\n-\t\tdone\n-\tdone &&\n-\tgit for-each-ref --ignore-case \\\n-\t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n-\t\t--sort=refname \\\n-\t\t--sort=subject \\\n-\t\t--sort=taggeremail \\\n-\t\trefs/tags/icase-* >actual &&\n-\tcat >expect <<-\\EOF &&\n-\t<a@example.com> tag a refs/tags/icase-00\n-\t<a@example.com> tag A refs/tags/icase-01\n-\t<A@example.com> tag a refs/tags/icase-04\n-\t<A@example.com> tag A refs/tags/icase-05\n-\t<a@example.com> tag b refs/tags/icase-02\n-\t<a@example.com> tag B refs/tags/icase-03\n-\t<A@example.com> tag b refs/tags/icase-06\n-\t<A@example.com> tag B refs/tags/icase-07\n-\t<b@example.com> tag a refs/tags/icase-08\n-\t<b@example.com> tag A refs/tags/icase-09\n-\t<B@example.com> tag a refs/tags/icase-12\n-\t<B@example.com> tag A refs/tags/icase-13\n-\t<b@example.com> tag b refs/tags/icase-10\n-\t<b@example.com> tag B refs/tags/icase-11\n-\t<B@example.com> tag b refs/tags/icase-14\n-\t<B@example.com> tag B refs/tags/icase-15\n-\tEOF\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref reports broken tags' '\n-\tgit tag -m \"good tag\" broken-tag-good HEAD &&\n-\tgit cat-file tag broken-tag-good >good &&\n-\tsed s/commit/blob/ <good >bad &&\n-\tbad=$(git hash-object -w -t tag bad) &&\n-\tgit update-ref refs/tags/broken-tag-bad $bad &&\n-\ttest_must_fail git for-each-ref --format=\"%(*objectname)\" \\\n-\t\trefs/tags/broken-tag-*\n-'\n-\n-test_expect_success 'set up tag with signature and no blank lines' '\n-\tgit tag -F - fake-sig-no-blanks <<-\\EOF\n-\tthis is the subject\n-\t-----BEGIN PGP SIGNATURE-----\n-\tnot a real signature, but we just care about the\n-\tsubject/body parsing. It is important here that\n-\tthere are no blank lines in the signature.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-'\n-\n-test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'\n-test_atom refs/tags/fake-sig-no-blanks contents:body ''\n-test_atom refs/tags/fake-sig-no-blanks contents:signature \"$sig\"\n-\n-test_expect_success 'set up tag with CRLF signature' '\n-\tappend_cr <<-\\EOF |\n-\tthis is the subject\n-\t-----BEGIN PGP SIGNATURE-----\n-\n-\tnot a real signature, but we just care about\n-\tthe subject/body parsing. It is important here\n-\tthat there is a blank line separating this\n-\tfrom the signature header.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-\tgit tag -F - --cleanup=verbatim fake-sig-crlf\n-'\n-\n-test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'\n-test_atom refs/tags/fake-sig-crlf contents:body ''\n-\n-# CRLF is retained in the signature, so we have to pass our expected value\n-# through append_cr. But test_atom requires a shell string, which means command\n-# substitution, and the shell will strip trailing newlines from the output of\n-# the substitution. Hack around it by adding and then removing a dummy line.\n-sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n-sig_crlf=${sig_crlf%dummy}\n-test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n-\n-test_expect_success 'set up tag with signature and trailers' '\n-\tgit tag -F - fake-sig-trailer <<-\\EOF\n-\tthis is the subject\n-\n-\tthis is the body\n-\n-\tMy-Trailer: foo\n-\t-----BEGIN PGP SIGNATURE-----\n-\n-\tnot a real signature, but we just care about the\n-\tsubject/body/trailer parsing.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-'\n-\n-# use \"separator=\" here to suppress the terminating newline\n-test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n-\n-test_expect_success 'git for-each-ref --stdin: empty' '\n-\t>in &&\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\tgit for-each-ref --format=\"%(refname)\" >expect &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'git for-each-ref --stdin: fails if extra args' '\n-\t>in &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname)\" \\\n-\t\t--stdin refs/heads/extra <in 2>err &&\n-\tgrep \"unknown arguments supplied with --stdin\" err\n-'\n-\n-test_expect_success 'git for-each-ref --stdin: matches' '\n-\tcat >in <<-EOF &&\n-\trefs/tags/multi*\n-\trefs/heads/amb*\n-\tEOF\n-\n-\tcat >expect <<-EOF &&\n-\trefs/heads/ambiguous\n-\trefs/tags/multi-ref1-100000-user1\n-\trefs/tags/multi-ref1-100000-user2\n-\trefs/tags/multi-ref1-200000-user1\n-\trefs/tags/multi-ref1-200000-user2\n-\trefs/tags/multi-ref2-100000-user1\n-\trefs/tags/multi-ref2-100000-user2\n-\trefs/tags/multi-ref2-200000-user1\n-\trefs/tags/multi-ref2-200000-user2\n-\trefs/tags/multiline\n-\tEOF\n-\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'git for-each-ref with non-existing refs' '\n-\tcat >in <<-EOF &&\n-\trefs/heads/this-ref-does-not-exist\n-\trefs/tags/bogus\n-\tEOF\n-\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\ttest_must_be_empty actual &&\n-\n-\txargs git for-each-ref --format=\"%(refname)\" <in >actual &&\n-\ttest_must_be_empty actual\n-'\n-\n-test_expect_success 'git for-each-ref with nested tags' '\n-\tgit tag -am \"Normal tag\" nested/base HEAD &&\n-\tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n-\tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n-\n-\thead_oid=\"$(git rev-parse HEAD)\" &&\n-\tbase_tag_oid=\"$(git rev-parse refs/tags/nested/base)\" &&\n-\tnest1_tag_oid=\"$(git rev-parse refs/tags/nested/nest1)\" &&\n-\tnest2_tag_oid=\"$(git rev-parse refs/tags/nested/nest2)\" &&\n-\n-\tcat >expect <<-EOF &&\n-\trefs/tags/nested/base $base_tag_oid tag $head_oid commit\n-\trefs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit\n-\trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n-\t\trefs/tags/nested/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'is-base atom with non-commits' '\n-\tgit for-each-ref --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n-\tgrep \"(HEAD) refs/heads/main\" out &&\n-\n-\ttest_line_count = 2 err &&\n-\tgrep \"error: object .* is a commit, not a blob\" err &&\n-\tgrep \"error: bad tag pointer to\" err\n-'\n-\n-GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n-TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n-\n-test_expect_success GPG 'setup for signature atom using gpg' '\n-\tgit checkout -b signed &&\n-\n-\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n-\n-\techo \"1\" >file &&\n-\tgit add file &&\n-\ttest_tick &&\n-\tgit commit -S -m \"file: 1\" &&\n-\tgit tag first-signed &&\n-\n-\techo \"2\" >file &&\n-\ttest_tick &&\n-\tgit commit -a -m \"file: 2\" &&\n-\tgit tag second-unsigned &&\n-\n-\tgit config commit.gpgSign 1 &&\n-\techo \"3\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n-\tgit tag third-unsigned &&\n-\n-\ttest_tick &&\n-\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n-\tgit tag third-signed &&\n-\n-\techo \"4\" >file &&\n-\ttest_tick &&\n-\tgit commit -a -SB7227189 -m \"file: 4\" &&\n-\tgit tag fourth-signed &&\n-\n-\techo \"5\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n-\tgit tag fifth-unsigned &&\n-\n-\techo \"6\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n-\n-\ttest_tick &&\n-\tgit rebase -f HEAD^^ &&\n-\tgit tag fifth-signed HEAD^ &&\n-\tgit tag sixth-signed &&\n-\n-\techo \"7\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n-\tgit tag seventh-unsigned\n-'\n-\n-test_expect_success GPGSSH 'setup for signature atom using ssh' '\n-\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n-\n-\ttest_config gpg.format ssh &&\n-\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n-\techo \"8\" >file &&\n-\ttest_tick &&\n-\tgit add file &&\n-\tgit commit -S -m \"file: 8\" &&\n-\tgit tag eighth-signed-ssh\n-'\n-\n-test_expect_success GPG2 'bare signature atom' '\n-\tgit verify-commit first-signed 2>expect &&\n-\techo  >>expect &&\n-\tgit for-each-ref refs/tags/first-signed \\\n-\t\t--format=\"%(signature)\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show good signature with custom format' '\n-\tgit verify-commit first-signed &&\n-\tcat >expect <<-\\EOF &&\n-\tG\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\tEOF\n-\tgit for-each-ref refs/tags/first-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n-\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n-\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n-\tcat >expect.tmpl <<-\\EOF &&\n-\tG\n-\tFINGERPRINT\n-\tprincipal with number 1\n-\tFINGERPRINT\n-\n-\tEOF\n-\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n-\tgit for-each-ref refs/tags/eighth-signed-ssh \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'signature atom with grade option and bad signature' '\n-\tgit cat-file commit third-signed >raw &&\n-\tsed -e \"s/^file: 3/file: 3 forged/\" raw >forged1 &&\n-\tFORGED1=$(git hash-object -w -t commit forged1) &&\n-\tgit update-ref refs/tags/third-signed \"$FORGED1\" &&\n-\ttest_must_fail git verify-commit \"$FORGED1\" &&\n-\n-\tcat >expect <<-\\EOF &&\n-\tB\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\n-\n-\tEOF\n-\tgit for-each-ref refs/tags/third-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tU\n-\t65A0EEA02E30CAD7\n-\tEris Discordia <discord@example.net>\n-\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n-\tD4BE22311AD3131E5EDA29A461092E85B7227189\n-\tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with undefined trust level' '\n-\tcat >expect <<-\\EOF &&\n-\tundefined\n-\t65A0EEA02E30CAD7\n-\tEris Discordia <discord@example.net>\n-\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n-\tD4BE22311AD3131E5EDA29A461092E85B7227189\n-\tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n-\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n-\tcat >expect <<-\\EOF &&\n-\tultimate\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\tEOF\n-\tgit for-each-ref refs/tags/sixth-signed \\\n-\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show unknown signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tE\n-\t13B6F51ECDDE430D\n-\n-\n-\n-\tEOF\n-\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n-\t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show lack of signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tN\n-\n-\n-\n-\n-\tEOF\n-\tgit for-each-ref refs/tags/seventh-unsigned \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n+. \"$TEST_DIRECTORY\"/for-each-ref-tests.sh\n \n test_done\n-- \n2.34.1\n\n"},{"id":"522527","messageId":"20250723064313.29866-4-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250723064313.29866-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v3 3/3] t: add test for git refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-23T06:43:13Z","receivedAt":"2025-07-23T06:43:33Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Add a test script, `t/t1461-refs-list.sh`, for the new `git refs list`\ncommand.\n\nThis script acts as a simple driver, leveraging the shared test library\ncreated in the preceding commit. It works by overriding the\n`$git_for_each_ref` variable to \"git refs list\" and then sourcing the\nshared library (`t/for-each-ref-tests.sh`).\n\nThis approach ensures that `git refs list` is tested against the\nentire comprehensive test suite of `git for-each-ref`, verifying\nthat it acts as a compatible drop-in replacement.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/meson.build        | 1 +\n t/t1461-refs-list.sh | 8 ++++++++\n 2 files changed, 9 insertions(+)\n create mode 100755 t/t1461-refs-list.sh\n\ndiff --git a/t/meson.build b/t/meson.build\nindex 50e89e764a..c959c039d0 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -224,6 +224,7 @@ integration_tests = [\n   't1450-fsck.sh',\n   't1451-fsck-buffer.sh',\n   't1460-refs-migrate.sh',\n+  't1461-refs-list.sh',\n   't1500-rev-parse.sh',\n   't1501-work-tree.sh',\n   't1502-rev-parse-parseopt.sh',\ndiff --git a/t/t1461-refs-list.sh b/t/t1461-refs-list.sh\nnew file mode 100755\nindex 0000000000..36e3d81e59\n--- /dev/null\n+++ b/t/t1461-refs-list.sh\n@@ -0,0 +1,8 @@\n+#!/bin/sh\n+\n+test_description='git refs list tests'\n+\n+. ./test-lib.sh\n+\n+git_for_each_ref='git refs list'\n+. \"$TEST_DIRECTORY\"/for-each-ref-tests.sh\n-- \n2.34.1\n\n"},{"id":"522571","messageId":"xmqqseinosp1.fsf@gitster.g","threadId":"63703","inReplyTo":"CAPhwyn0wLrTy0MnNYdQvoVPXUz0PyXDB0MBsDHnDR_zgA8mZmg@mail.gmail.com","subject":"Re: [GSoC][RFC PATCH v2 2/2] t: add test for git refs list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-23T15:03:22Z","receivedAt":"2025-07-23T15:03:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Meet Soni <meetsoni3017@gmail.com> writes:\n\n> Apologies for the delayed response, I was away last week.\n\nNo need to apologize.  Everybody knows that summer is a slow season.\n\nThanks for your contribution; enjoy polishing your topic.\n"},{"id":"522625","messageId":"aIHLdkhdVNy72Yf-@pks.im","threadId":"63703","inReplyTo":"20250723064313.29866-2-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH v3 1/3] builtin/refs: add list subcommand","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-24T05:58:14Z","receivedAt":"2025-07-24T05:58:27Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Jul 23, 2025 at 12:13:11PM +0530, Meet Soni wrote:\n> diff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\n> index 5ef89fc0fe..f7bbc1902a 100644\n> --- a/Documentation/git-for-each-ref.adoc\n> +++ b/Documentation/git-for-each-ref.adoc\n\nTiny nit, not worth a reroll by itself: it would have been nice to move\nthe extraction of the common options from our docs into a separate,\npreparatory commit.\n\n> diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\n> index 3d2207ec77..d7d8279049 100644\n> --- a/builtin/for-each-ref.c\n> +++ b/builtin/for-each-ref.c\n> @@ -16,11 +16,27 @@ static char const * const for_each_ref_usage[] = {\n>  \tNULL\n>  };\n>  \n> +#define REFS_LIST_USAGE \\\n> +\tN_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n> +\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n> +\t   \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n> +\t   \"              [--points-at=<object>]\\n\" \\\n> +\t   \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n> +\t   \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n> +\t   \"              [--exclude=<pattern> ...]\")\n> +\n> +static char const * const refs_list_usage[] = {\n> +\tREFS_LIST_USAGE,\n> +\tNULL\n> +};\n\nShouldn't the usage strings for git-for-each-ref(1) and git-refs-list(1)\nbe the same, except for the command name?\n\n>  int cmd_for_each_ref(int argc,\n>  \t\t     const char **argv,\n>  \t\t     const char *prefix,\n>  \t\t     struct repository *repo)\n>  {\n> +\tint cmd_is_refs_list = !strcmp(argv[0], \"refs list\");\n> +\tconst char *const *opt_usage = cmd_is_refs_list ? refs_list_usage : for_each_ref_usage;\n>  \tstruct ref_sorting *sorting;\n>  \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n>  \tint icase = 0, include_root_refs = 0, from_stdin = 0;\n\nThis follows the same pattern we have in \"builtin/blame.c\". It's not\nexactly pretty that git-for-each-ref(1) is aware of git-refs(1) now, but\nI think it's the pragmatic thing to do.\n\n> diff --git a/builtin/refs.c b/builtin/refs.c\n> index 998d2a2c1c..41e29d1b5f 100644\n> --- a/builtin/refs.c\n> +++ b/builtin/refs.c\n> @@ -13,6 +14,15 @@\n>  #define REFS_VERIFY_USAGE \\\n>  \tN_(\"git refs verify [--strict] [--verbose]\")\n>  \n> +#define REFS_LIST_USAGE \\\n> +\tN_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n> +\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n> +\t   \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n> +\t   \"              [--points-at=<object>]\\n\" \\\n> +\t   \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n> +\t   \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n> +\t   \"              [--exclude=<pattern> ...]\")\n> +\n>  static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,\n>  \t\t\t    struct repository *repo UNUSED)\n>  {\n\nHm, this one is a bit unfortunate though, as it feels like it's just a\nmatter of time before the two `REFS_LIST_USAGE` defines drift apart.\nMight be worth it to move them to a shared place.\n\nAlternatively, we could pull out the logic of `cmd_for_each_ref()` into\na separate function that also receives the usage array. Not sure whether\nthat is worth the hassle though.\n\nAnother alternative would be to just say `git refs list [<options>]`.\nThis here \n"},{"id":"522684","messageId":"xmqq4iv1k26x.fsf@gitster.g","threadId":"63703","inReplyTo":"aIHLdkhdVNy72Yf-@pks.im","subject":"Re: [GSoC][RFC PATCH v3 1/3] builtin/refs: add list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-24T16:01:42Z","receivedAt":"2025-07-24T16:01:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Wed, Jul 23, 2025 at 12:13:11PM +0530, Meet Soni wrote:\n>> diff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\n>> index 5ef89fc0fe..f7bbc1902a 100644\n>> --- a/Documentation/git-for-each-ref.adoc\n>> +++ b/Documentation/git-for-each-ref.adoc\n>\n> Tiny nit, not worth a reroll by itself: it would have been nice to move\n> the extraction of the common options from our docs into a separate,\n> preparatory commit.\n\nTrue.\n\n>> diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\n>> index 3d2207ec77..d7d8279049 100644\n>> --- a/builtin/for-each-ref.c\n>> +++ b/builtin/for-each-ref.c\n>> @@ -16,11 +16,27 @@ static char const * const for_each_ref_usage[] = {\n>>  \tNULL\n>>  };\n>>  \n>> +#define REFS_LIST_USAGE \\\n>> +\tN_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n>> +\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n>> +\t   \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n>> +\t   \"              [--points-at=<object>]\\n\" \\\n>> +\t   \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n>> +\t   \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n>> +\t   \"              [--exclude=<pattern> ...]\")\n>> +\n>> +static char const * const refs_list_usage[] = {\n>> +\tREFS_LIST_USAGE,\n>> +\tNULL\n>> +};\n>\n> Shouldn't the usage strings for git-for-each-ref(1) and git-refs-list(1)\n> be the same, except for the command name?\n\nA great observation, but where would it lead us?  Something like\n\n        #define COMMON_USAGE_FOR_EACH_REF \\\n                   \" [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n\t\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n                   ... \\\n                   \"              [--exclude=<pattern> ...]\"\n\n        #define REFS_LIST_USAGE \\\n                N_(\"git refs list\" COMMON_USAGE_FOR_EACH_REF);\n        #define FOR_EACH_REF_USAGE \\\n                N_(\"git for-each-ref\" COMMON_USAGE_FOR_EACH_REF);\n\nis very much desirable because we do not want the options of these\ntwo commands drift apart, but it does not quite work with the\ncurrent usage_with_options() infrastructure, as it leaves the\nindentation of second and subsequent lines up to the caller, and the\ndisplay widths of \"refs list\" and \"for-each-ref\" are different.\n\n>>  int cmd_for_each_ref(int argc,\n>>  \t\t     const char **argv,\n>>  \t\t     const char *prefix,\n>>  \t\t     struct repository *repo)\n>>  {\n>> +\tint cmd_is_refs_list = !strcmp(argv[0], \"refs list\");\n>> +\tconst char *const *opt_usage = cmd_is_refs_list ? refs_list_usage : for_each_ref_usage;\n>>  \tstruct ref_sorting *sorting;\n>>  \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n>>  \tint icase = 0, include_root_refs = 0, from_stdin = 0;\n>\n> This follows the same pattern we have in \"builtin/blame.c\". It's not\n> exactly pretty that git-for-each-ref(1) is aware of git-refs(1) now, but\n> I think it's the pragmatic thing to do.\n\nAlthough it is yucky.  argv[0] can never be \"refs list\" unless you\nare calling this as if it were a subroutine by hand, which is in\ngeneral a no-no.\n\ncf. https://lore.kernel.org/git/20171010040604.26029-1-gitster@pobox.com/\n\nIn this particular case, it is not like calling cmd_merge() from\nwithin a loop in cmd_rebase(), so many reasons against such program\nstructure would not apply, but it would be prudent to leave an\nin-code comment to warn others not to mimic this pattern in general\ncase.\n\n>> diff --git a/builtin/refs.c b/builtin/refs.c\n>> index 998d2a2c1c..41e29d1b5f 100644\n>> --- a/builtin/refs.c\n>> +++ b/builtin/refs.c\n>> @@ -13,6 +14,15 @@\n>>  #define REFS_VERIFY_USAGE \\\n>>  \tN_(\"git refs verify [--strict] [--verbose]\")\n>>  \n>> +#define REFS_LIST_USAGE \\\n>> +\tN_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n>> +\t   \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n>> +\t   \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n>> +\t   \"              [--points-at=<object>]\\n\" \\\n>> +\t   \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n>> +\t   \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n>> +\t   \"              [--exclude=<pattern> ...]\")\n>> +\n>>  static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,\n>>  \t\t\t    struct repository *repo UNUSED)\n>>  {\n>\n> Hm, this one is a bit unfortunate though, as it feels like it's just a\n> matter of time before the two `REFS_LIST_USAGE` defines drift apart.\n> Might be worth it to move them to a shared place.\n\nYes, but see above.\n\n> Alternatively, we could pull out the logic of `cmd_for_each_ref()` into\n> a separate function that also receives the usage array. Not sure whether\n> that is worth the hassle though.\n\nI was also wondering about such restructuring of the existing\nprogram.  Before adding a single line to support \"refs list\", can we\nmove much of what cmd_for_each_ref() does that is common between it\nand upcoming \"refs list\" into a helper function, and make\ncmd_for_each_ref() to call it, as a preparatory step?  Then a new\n\"refs list\" implementation does not have to touch the implementation\nof cmd_for_each_ref(), or call cmd_for_each_ref().  Which would give\nus much cleaner implementation, and we do not have to leave an ugly\ncomment \"we are doing this because we want to introduce 'refs list'\nsynonym that behaves identically and calling cmd_for_each_ref() is\nthe easiest way, but do not mimick it! It is an unacceptable pattern\nyou should not follow in more general cases\".\n\nThanks.\n"},{"id":"522739","messageId":"CAPhwyn1XK8SoW_EM+EnaaxWsQ8QTYVkMxBZqCQa0-YutvNVJZA@mail.gmail.com","threadId":"63703","inReplyTo":"aIHLdkhdVNy72Yf-@pks.im","subject":"Re: [GSoC][RFC PATCH v3 1/3] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-25T11:10:18Z","receivedAt":"2025-07-25T11:10:31Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"On Thu, 24 Jul 2025 at 11:28, Patrick Steinhardt <ps@pks.im> wrote:\n>\n> On Wed, Jul 23, 2025 at 12:13:11PM +0530, Meet Soni wrote:\n> > diff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\n> > index 5ef89fc0fe..f7bbc1902a 100644\n> > --- a/Documentation/git-for-each-ref.adoc\n> > +++ b/Documentation/git-for-each-ref.adoc\n>\n> Tiny nit, not worth a reroll by itself: it would have been nice to move\n> the extraction of the common options from our docs into a separate,\n> preparatory commit.\n>\nI'll do it in the next version.\n\n> > diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\n> > index 3d2207ec77..d7d8279049 100644\n> > --- a/builtin/for-each-ref.c\n> > +++ b/builtin/for-each-ref.c\n> > @@ -16,11 +16,27 @@ static char const * const for_each_ref_usage[] = {\n> >       NULL\n> >  };\n> >\n> > +#define REFS_LIST_USAGE \\\n> > +     N_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n> > +        \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n> > +        \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n> > +        \"              [--points-at=<object>]\\n\" \\\n> > +        \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n> > +        \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n> > +        \"              [--exclude=<pattern> ...]\")\n> > +\n> > +static char const * const refs_list_usage[] = {\n> > +     REFS_LIST_USAGE,\n> > +     NULL\n> > +};\n>\n> Shouldn't the usage strings for git-for-each-ref(1) and git-refs-list(1)\n> be the same, except for the command name?\n>\nI thought so too, but the man page of git-for-each-ref(1) and the code\nimplementation\ndiffer. And the test didn't capture it as it's marked as a \"known breakage\",\nwill update this in the next version.\n\n> >  int cmd_for_each_ref(int argc,\n> >                    const char **argv,\n> >                    const char *prefix,\n> >                    struct repository *repo)\n> >  {\n> > +     int cmd_is_refs_list = !strcmp(argv[0], \"refs list\");\n> > +     const char *const *opt_usage = cmd_is_refs_list ? refs_list_usage : for_each_ref_usage;\n> >       struct ref_sorting *sorting;\n> >       struct string_list sorting_options = STRING_LIST_INIT_DUP;\n> >       int icase = 0, include_root_refs = 0, from_stdin = 0;\n>\n> This follows the same pattern we have in \"builtin/blame.c\". It's not\n> exactly pretty that git-for-each-ref(1) is aware of git-refs(1) now, but\n> I think it's the pragmatic thing to do.\n>\n> > diff --git a/builtin/refs.c b/builtin/refs.c\n> > index 998d2a2c1c..41e29d1b5f 100644\n> > --- a/builtin/refs.c\n> > +++ b/builtin/refs.c\n> > @@ -13,6 +14,15 @@\n> >  #define REFS_VERIFY_USAGE \\\n> >       N_(\"git refs verify [--strict] [--verbose]\")\n> >\n> > +#define REFS_LIST_USAGE \\\n> > +     N_(\"git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n> > +        \"              [(--sort=<key>)...] [--format=<format>]\\n\" \\\n> > +        \"              [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n> > +        \"              [--points-at=<object>]\\n\" \\\n> > +        \"              [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n> > +        \"              [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n> > +        \"              [--exclude=<pattern> ...]\")\n> > +\n> >  static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,\n> >                           struct repository *repo UNUSED)\n> >  {\n>\n> Hm, this one is a bit unfortunate though, as it feels like it's just a\n> matter of time before the two `REFS_LIST_USAGE` defines drift apart.\n> Might be worth it to move them to a shared place.\n>\n> Alternatively, we could pull out the logic of `cmd_for_each_ref()` into\n> a separate function that also receives the usage array. Not sure whether\n> that is worth the hassle though.\n>\n> Another alternative would be to just say `git refs list [<options>]`.\n> This here\n\nTrue.\nThe first two suggestions seem the right way forward. The third one,\njust using a generic\ngit refs list [<options>] seems counter-productive, as a key goal here\nis to improve\ndiscoverability, and hiding the full list of options would work against that.\n\nThe solution of pulling the core logic into a helper function seems\nlike the most robust\napproach, and since Junio's feedback also points in that direction,\nI'll proceed with that\nrefactoring.\n\nThanks.\n"},{"id":"523084","messageId":"20250731090040.1625303-1-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250723064313.29866-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v4 0/5] Add refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-31T09:00:35Z","receivedAt":"2025-07-31T09:00:47Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Hello everyone,\n\nThis is the fourth version of the patch series that introduces the git\nrefs list subcommand.\n\nChanges in v4:\n\n  - Implemented architectural refactoring. The core logic of\n    for-each-ref now resides in a shared helper function, and both\n    for-each-ref and refs list have been simplified to thin wrappers\n    around it.\n\n  - The usage strings have also been refactored. The common options\n    are now defined in a shared macro in a new for-each-ref.h header.\n\n  - The patch series has been further split to cleanly separate the\n    preparatory refactoring commits (for both the C code and the\n    AsciiDoc documentation) from the commit that introduces the new\n    feature.\n\n  - As a beneficial side-effect of unifying the usage strings, this\n    series now fixes a pre-existing inconsistency between the --help\n    output and the man page for for-each-ref. This allows a known\n    breakage for it in the t0450 documentation test to be removed.\n\n---\n\n(v1 cover-letter text)\n\nThis patch series introduces `git refs list` as a modern replacement for\n`git for-each-ref`, as part of an effort to consolidate ref-related\nfunctionality under a unified `git refs` command.\n\nGit's ref-related operations are currently handled by several distinct\ncommands, such as `git show-ref`, `git for-each-ref`, `git update-ref`,\n`git pack-refs`, etc. This distribution has a few practical drawbacks:\n\n- Users need to rely on multiple commands for related tasks involving\n  refs.\n\n- The commands may differ slightly in behavior and option syntax,\n  leading to inconsistency.\n\nWe propose a long-term consolidation effort to bring ref-related\nsubcommands under the umbrella of a single command: `git refs`.\n\nThe implementation of `git refs list` is functionally identical to `git\nfor-each-ref`. It reuses the same internal logic (cmd_for_each_ref) to\nensure complete backward compatibility. The purpose of this patch is not\nto introduce new behavior but to provide an alternate entry point under\nthe consolidated `git refs` namespace.\n\nThe motivation behind this change is twofold:\n\n- Consolidation: Centralizing ref-related operations makes them easier\n  to discover, use, and maintain.\n\n- Evolution: While the initial goal is parity with existing commands,\n  this consolidation allows for careful reconsideration of which\n  features are essential. Over time, we can:\n\n  - Remove legacy or obscure options that are no longer needed.\n  - Add improvements that wouldn't make sense to bolt onto legacy\n    commands.\n  - Offering a more consistent and user-friendly surface.\n\nTo verify backward compatibility, this patch also includes a test\n`t/t1461-refs-list.sh`, which runs the full `t6300-for-each-ref.sh` test\nusing `git refs list`. The test uses ${GIT_REFS_LIST_CMD:-for-each-ref}\nto allow substitution without duplicating tests.\n\nThis patch is deliberately conservative: it introduces no behavioral\nchanges and leaves `for-each-ref` untouched. The goal is to lay\ngroundwork and demonstrate viability of ref consolidation within `git\nrefs`.\n\nGoing forward, I'd like to initiate a discussion on what the ideal\nsurface of `git refs list` should look like. Which options and features\nfrom `for-each-ref` should be carried over? Are there any that are\nobsolete or overly niche? What improvements might be worth considering\nnow that we have a new, consolidated interface?\n\nFeedback on this, especially from those who rely on `for-each-ref` in\nscripts or tooling would be very helpful.\n\nMeet Soni (5):\n  doc: factor out common option\n  builtin/for-each-ref: factor out core logic into a helper\n  builtin/refs: add list subcommand\n  t6300: refactor tests to be shareable\n  t: add test for git refs list subcommand\n\n Documentation/for-each-ref-options.adoc |   79 +\n Documentation/git-for-each-ref.adoc     |   80 +-\n Documentation/git-refs.adoc             |   16 +\n builtin/for-each-ref.c                  |   35 +-\n builtin/refs.c                          |   14 +\n for-each-ref.h                          |   24 +\n t/for-each-ref-tests.sh                 | 2141 +++++++++++++++++++++++\n t/meson.build                           |    1 +\n t/t0450/adoc-help-mismatches            |    1 -\n t/t1461-refs-list.sh                    |    8 +\n t/t6300-for-each-ref.sh                 | 2140 +---------------------\n 11 files changed, 2305 insertions(+), 2234 deletions(-)\n create mode 100644 Documentation/for-each-ref-options.adoc\n create mode 100644 for-each-ref.h\n create mode 100644 t/for-each-ref-tests.sh\n create mode 100755 t/t1461-refs-list.sh\n\nRange-diff against v3:\n1:  547b0bbf8f < -:  ---------- builtin/refs: add list subcommand\n-:  ---------- > 1:  d2fa47a2b9 doc: factor out common option\n-:  ---------- > 2:  5084db4d14 builtin/for-each-ref: factor out core logic into a helper\n-:  ---------- > 3:  1a15cd454b builtin/refs: add list subcommand\n2:  5d5057ff98 = 4:  29c41d682b t6300: refactor tests to be shareable\n3:  b9cb9cdf48 = 5:  0fd1f714c9 t: add test for git refs list subcommand\n-- \n2.34.1\n\n"},{"id":"523085","messageId":"20250731090040.1625303-2-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250731090040.1625303-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v4 1/5] doc: factor out common option","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-31T09:00:36Z","receivedAt":"2025-07-31T09:00:52Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"In preparation for adding documentation for `git refs list`, factor out\nthe common options from the `git-for-each-ref` man page into a\nshareable file `for-each-ref-options.adoc` and update\n`git-for-each-ref.adoc` to use an `include::` macro.\n\nThis change is a pure refactoring and results in no change to the\nfinal rendered documentation for `for-each-ref`.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/for-each-ref-options.adoc | 79 ++++++++++++++++++++++++\n Documentation/git-for-each-ref.adoc     | 80 +------------------------\n 2 files changed, 80 insertions(+), 79 deletions(-)\n create mode 100644 Documentation/for-each-ref-options.adoc\n\ndiff --git a/Documentation/for-each-ref-options.adoc b/Documentation/for-each-ref-options.adoc\nnew file mode 100644\nindex 0000000000..5f3a85bf64\n--- /dev/null\n+++ b/Documentation/for-each-ref-options.adoc\n@@ -0,0 +1,79 @@\n+<pattern>...::\n+\tIf one or more patterns are given, only refs are shown that\n+\tmatch against at least one pattern, either using fnmatch(3) or\n+\tliterally, in the latter case matching completely or from the\n+\tbeginning up to a slash.\n+\n+--stdin::\n+\tIf `--stdin` is supplied, then the list of patterns is read from\n+\tstandard input instead of from the argument list.\n+\n+--count=<count>::\n+\tBy default the command shows all refs that match\n+\t`<pattern>`.  This option makes it stop after showing\n+\tthat many refs.\n+\n+--sort=<key>::\n+\tA field name to sort on.  Prefix `-` to sort in\n+\tdescending order of the value.  When unspecified,\n+\t`refname` is used.  You may use the --sort=<key> option\n+\tmultiple times, in which case the last key becomes the primary\n+\tkey.\n+\n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from a ref being shown and\n+\tthe object it points at. In addition, the string literal `%%`\n+\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n+\tthe character with hex code `xx`. For example, `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n++\n+When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n+TAB %(refname)`.\n+\n+--color[=<when>]::\n+\tRespect any colors specified in the `--format` option. The\n+\t`<when>` field must be one of `always`, `never`, or `auto` (if\n+\t`<when>` is absent, behave as if `always` was given).\n+\n+--shell::\n+--perl::\n+--python::\n+--tcl::\n+\tIf given, strings that substitute `%(fieldname)`\n+\tplaceholders are quoted as string literals suitable for\n+\tthe specified host language.  This is meant to produce\n+\ta scriptlet that can directly be `eval`ed.\n+\n+--points-at=<object>::\n+\tOnly list refs which points at the given object.\n+\n+--merged[=<object>]::\n+\tOnly list refs whose tips are reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--no-merged[=<object>]::\n+\tOnly list refs whose tips are not reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--contains[=<object>]::\n+\tOnly list refs which contain the specified commit (HEAD if not\n+\tspecified).\n+\n+--no-contains[=<object>]::\n+\tOnly list refs which don't contain the specified commit (HEAD\n+\tif not specified).\n+\n+--ignore-case::\n+\tSorting and filtering refs are case insensitive.\n+\n+--omit-empty::\n+\tDo not print a newline after formatted refs where the format expands\n+\tto the empty string.\n+\n+--exclude=<pattern>::\n+\tIf one or more patterns are given, only refs which do not match\n+\tany excluded pattern(s) are shown. Matching is done using the\n+\tsame rules as `<pattern>` above.\n+\n+--include-root-refs::\n+\tList root refs (HEAD and pseudorefs) apart from regular refs.\ndiff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\nindex 5ef89fc0fe..c3cf1752e3 100644\n--- a/Documentation/git-for-each-ref.adoc\n+++ b/Documentation/git-for-each-ref.adoc\n@@ -28,85 +28,7 @@ host language allowing their direct evaluation in that language.\n \n OPTIONS\n -------\n-<pattern>...::\n-\tIf one or more patterns are given, only refs are shown that\n-\tmatch against at least one pattern, either using fnmatch(3) or\n-\tliterally, in the latter case matching completely or from the\n-\tbeginning up to a slash.\n-\n---stdin::\n-\tIf `--stdin` is supplied, then the list of patterns is read from\n-\tstandard input instead of from the argument list.\n-\n---count=<count>::\n-\tBy default the command shows all refs that match\n-\t`<pattern>`.  This option makes it stop after showing\n-\tthat many refs.\n-\n---sort=<key>::\n-\tA field name to sort on.  Prefix `-` to sort in\n-\tdescending order of the value.  When unspecified,\n-\t`refname` is used.  You may use the --sort=<key> option\n-\tmultiple times, in which case the last key becomes the primary\n-\tkey.\n-\n---format=<format>::\n-\tA string that interpolates `%(fieldname)` from a ref being shown and\n-\tthe object it points at. In addition, the string literal `%%`\n-\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n-\tthe character with hex code `xx`. For example, `%00` interpolates to\n-\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n-+\n-When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n-TAB %(refname)`.\n-\n---color[=<when>]::\n-\tRespect any colors specified in the `--format` option. The\n-\t`<when>` field must be one of `always`, `never`, or `auto` (if\n-\t`<when>` is absent, behave as if `always` was given).\n-\n---shell::\n---perl::\n---python::\n---tcl::\n-\tIf given, strings that substitute `%(fieldname)`\n-\tplaceholders are quoted as string literals suitable for\n-\tthe specified host language.  This is meant to produce\n-\ta scriptlet that can directly be `eval`ed.\n-\n---points-at=<object>::\n-\tOnly list refs which points at the given object.\n-\n---merged[=<object>]::\n-\tOnly list refs whose tips are reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---no-merged[=<object>]::\n-\tOnly list refs whose tips are not reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---contains[=<object>]::\n-\tOnly list refs which contain the specified commit (HEAD if not\n-\tspecified).\n-\n---no-contains[=<object>]::\n-\tOnly list refs which don't contain the specified commit (HEAD\n-\tif not specified).\n-\n---ignore-case::\n-\tSorting and filtering refs are case insensitive.\n-\n---omit-empty::\n-\tDo not print a newline after formatted refs where the format expands\n-\tto the empty string.\n-\n---exclude=<pattern>::\n-\tIf one or more patterns are given, only refs which do not match\n-\tany excluded pattern(s) are shown. Matching is done using the\n-\tsame rules as `<pattern>` above.\n-\n---include-root-refs::\n-\tList root refs (HEAD and pseudorefs) apart from regular refs.\n+include::for-each-ref-options.adoc[]\n \n FIELD NAMES\n -----------\n-- \n2.34.1\n\n"},{"id":"523086","messageId":"20250731090040.1625303-3-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250731090040.1625303-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v4 2/5] builtin/for-each-ref: factor out core logic into a helper","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-31T09:00:37Z","receivedAt":"2025-07-31T09:00:56Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"The implementation of `git for-each-ref` is monolithic within\n`cmd_for_each_ref()`, making it impossible to share its logic with other\ncommands. To enable code reuse for the upcoming `git refs list`\nsubcommand, refactor the core logic into a shared helper function.\n\nIntroduce a new `for-each-ref.h` header to define the public interface\nfor this shared logic. It contains the declaration for a new helper\nfunction, `for_each_ref_core()`, and a macro for the common usage\noptions.\n\nMove the option parsing, filtering, and formatting logic from\n`cmd_for_each_ref()` into a new helper function named\n`for_each_ref_core()`. This helper is made generic by accepting the\ncommand's usage string as a parameter.\n\nThe original `cmd_for_each_ref()` is simplified to a thin wrapper that\nis only responsible for defining its specific usage array and calling\nthe shared helper.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n builtin/for-each-ref.c       | 35 +++++++++++++++++++----------------\n for-each-ref.h               | 24 ++++++++++++++++++++++++\n t/t0450/adoc-help-mismatches |  1 -\n 3 files changed, 43 insertions(+), 17 deletions(-)\n create mode 100644 for-each-ref.h\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 3d2207ec77..bbc0e5ad1c 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -7,19 +7,9 @@\n #include \"ref-filter.h\"\n #include \"strbuf.h\"\n #include \"strvec.h\"\n+#include \"for-each-ref.h\"\n \n-static char const * const for_each_ref_usage[] = {\n-\tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n-\tN_(\"git for-each-ref [--points-at <object>]\"),\n-\tN_(\"git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]\"),\n-\tN_(\"git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]\"),\n-\tNULL\n-};\n-\n-int cmd_for_each_ref(int argc,\n-\t\t     const char **argv,\n-\t\t     const char *prefix,\n-\t\t     struct repository *repo)\n+int for_each_ref_core(int argc, const char **argv, const char *prefix, struct repository *repo, const char *const *usage)\n {\n \tstruct ref_sorting *sorting;\n \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n@@ -67,17 +57,17 @@ int cmd_for_each_ref(int argc,\n \t/* Set default (refname) sorting */\n \tstring_list_append(&sorting_options, \"refname\");\n \n-\tparse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);\n+\tparse_options(argc, argv, prefix, opts, usage, 0);\n \tif (format.array_opts.max_count < 0) {\n \t\terror(\"invalid --count argument: `%d'\", format.array_opts.max_count);\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(usage, opts);\n \t}\n \tif (HAS_MULTI_BITS(format.quote_style)) {\n \t\terror(\"more than one quoting style?\");\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(usage, opts);\n \t}\n \tif (verify_ref_format(&format))\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(usage, opts);\n \n \tsorting = ref_sorting_options(&sorting_options);\n \tref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);\n@@ -111,3 +101,16 @@ int cmd_for_each_ref(int argc,\n \tstrvec_clear(&vec);\n \treturn 0;\n }\n+\n+int cmd_for_each_ref(int argc,\n+\t\t     const char **argv,\n+\t\t     const char *prefix,\n+\t\t     struct repository *repo)\n+{\n+\tstatic char const * const for_each_ref_usage[] = {\n+\t\tN_(\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF),\n+\t\tNULL\n+\t};\n+\n+\treturn for_each_ref_core(argc, argv, prefix, repo, for_each_ref_usage);\n+}\ndiff --git a/for-each-ref.h b/for-each-ref.h\nnew file mode 100644\nindex 0000000000..06d5b123b3\n--- /dev/null\n+++ b/for-each-ref.h\n@@ -0,0 +1,24 @@\n+#ifndef FOR_EACH_REF_H\n+#define FOR_EACH_REF_H\n+\n+/*\n+ * Shared usage string for options common to git-for-each-ref(1)\n+ * and git-refs-list(1). The command-specific part (e.g., \"git refs list \")\n+ * must be prepended by the caller.\n+ */\n+#define COMMON_USAGE_FOR_EACH_REF \\\n+\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t\"                         [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n+\t\"                         [--points-at=<object>]\\n\" \\\n+\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t\"                         [--exclude=<pattern> ...]\"\n+\n+/*\n+ * The core logic for for-each-ref and its clones.\n+ */\n+int for_each_ref_core(int argc, const char **argv, const char *prefix,\n+\t\t      struct repository *repo, const char *const *usage);\n+\n+#endif /* FOR_EACH_REF_H */\ndiff --git a/t/t0450/adoc-help-mismatches b/t/t0450/adoc-help-mismatches\nindex 06b469bdee..2c6ecd5fc8 100644\n--- a/t/t0450/adoc-help-mismatches\n+++ b/t/t0450/adoc-help-mismatches\n@@ -17,7 +17,6 @@ fast-export\n fast-import\n fetch-pack\n fmt-merge-msg\n-for-each-ref\n format-patch\n fsck-objects\n fsmonitor--daemon\n-- \n2.34.1\n\n"},{"id":"523087","messageId":"20250731090040.1625303-4-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250731090040.1625303-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v4 3/5] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-31T09:00:38Z","receivedAt":"2025-07-31T09:01:00Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Git's reference management is distributed across multiple commands. As\npart of an ongoing effort to consolidate and modernize reference\nhandling, introduce a `list` subcommand under the `git refs` umbrella as\na replacement for `git for-each-ref`.\n\nImplement `cmd_refs_list` by having it call the `for_each_ref_core()`\nhelper function. This helper was factored out of the original\n`cmd_for_each_ref` in a preceding commit, allowing both commands to\nshare the same core logic as independent peers.\n\nAdd documentation for the new command. The man page leverages the shared\noptions file, created in a previous commit, by using the AsciiDoc\n`include::` macro to ensure consistency with git-for-each-ref(1).\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/git-refs.adoc | 16 ++++++++++++++++\n builtin/refs.c              | 14 ++++++++++++++\n 2 files changed, 30 insertions(+)\n\ndiff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\nindex 4d6dc994f9..ee563aa7e0 100644\n--- a/Documentation/git-refs.adoc\n+++ b/Documentation/git-refs.adoc\n@@ -11,6 +11,13 @@ SYNOPSIS\n [synopsis]\n git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n git refs verify [--strict] [--verbose]\n+git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n+\t      [(--sort=<key>)...] [--format=<format>]\n+\t      [--include-root-refs] [ --stdin | <pattern>... ]\n+\t      [--points-at=<object>]\n+\t      [--merged[=<object>]] [--no-merged[=<object>]]\n+\t      [--contains[=<object>]] [--no-contains[=<object>]]\n+\t      [--exclude=<pattern> ...]\n \n DESCRIPTION\n -----------\n@@ -26,6 +33,11 @@ migrate::\n verify::\n \tVerify reference database consistency.\n \n+list::\n+\tList references in the repository with support for filtering,\n+\tformatting, and sorting. This subcommand is an alias for\n+\tlinkgit:git-for-each-ref[1] and offers identical functionality.\n+\n OPTIONS\n -------\n \n@@ -57,6 +69,10 @@ The following options are specific to 'git refs verify':\n --verbose::\n \tWhen verifying the reference database consistency, be chatty.\n \n+The following options are specific to 'git refs list':\n+\n+include::for-each-ref-options.adoc[]\n+\n KNOWN LIMITATIONS\n -----------------\n \ndiff --git a/builtin/refs.c b/builtin/refs.c\nindex 998d2a2c1c..848a7c9072 100644\n--- a/builtin/refs.c\n+++ b/builtin/refs.c\n@@ -6,6 +6,7 @@\n #include \"refs.h\"\n #include \"strbuf.h\"\n #include \"worktree.h\"\n+#include \"for-each-ref.h\"\n \n #define REFS_MIGRATE_USAGE \\\n \tN_(\"git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\")\n@@ -101,6 +102,17 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,\n \treturn ret;\n }\n \n+static int cmd_refs_list(int argc, const char **argv, const char *prefix,\n+\t\t\t   struct repository *repo)\n+{\n+\tstatic char const * const refs_list_usage[] = {\n+\t\tN_(\"git refs list \" COMMON_USAGE_FOR_EACH_REF),\n+\t\tNULL\n+\t};\n+\n+\treturn for_each_ref_core(argc, argv, prefix, repo, refs_list_usage);\n+}\n+\n int cmd_refs(int argc,\n \t     const char **argv,\n \t     const char *prefix,\n@@ -109,12 +121,14 @@ int cmd_refs(int argc,\n \tconst char * const refs_usage[] = {\n \t\tREFS_MIGRATE_USAGE,\n \t\tREFS_VERIFY_USAGE,\n+\t\t\"git refs list \" COMMON_USAGE_FOR_EACH_REF,\n \t\tNULL,\n \t};\n \tparse_opt_subcommand_fn *fn = NULL;\n \tstruct option opts[] = {\n \t\tOPT_SUBCOMMAND(\"migrate\", &fn, cmd_refs_migrate),\n \t\tOPT_SUBCOMMAND(\"verify\", &fn, cmd_refs_verify),\n+\t\tOPT_SUBCOMMAND(\"list\", &fn, cmd_refs_list),\n \t\tOPT_END(),\n \t};\n \n-- \n2.34.1\n\n"},{"id":"523089","messageId":"20250731090040.1625303-5-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250731090040.1625303-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v4 4/5] t6300: refactor tests to be shareable","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-31T09:00:39Z","receivedAt":"2025-07-31T09:01:06Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"In preparation for adding tests for the new `git refs list` command,\nrefactor the existing t6300 test suite to make its logic shareable.\n\nMove the core test logic from `t6300-for-each-ref.sh` into a new\n`for-each-ref-tests.sh` file. Inside this new script, replace hardcoded\ncalls to \"git for-each-ref\" with the `$git_for_each_ref` variable.\n\nThe original `t6300-for-each-ref.sh` script now becomes a simple\n\"driver\". It is responsible for setting the default value of the\nvariable and then sourcing the test library.\n\nThis new structure follows the established pattern used for sharing\ntests between `git-blame` and `git-annotate` and prepares the test suite\nfor the `refs list` tests to be added in a subsequent commit.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/for-each-ref-tests.sh | 2141 +++++++++++++++++++++++++++++++++++++++\n t/t6300-for-each-ref.sh | 2140 +-------------------------------------\n 2 files changed, 2143 insertions(+), 2138 deletions(-)\n create mode 100644 t/for-each-ref-tests.sh\n\ndiff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh\nnew file mode 100644\nindex 0000000000..e3ad19298a\n--- /dev/null\n+++ b/t/for-each-ref-tests.sh\n@@ -0,0 +1,2141 @@\n+git_for_each_ref=${git_for_each_ref:-git for-each-ref}\n+GNUPGHOME_NOT_USED=$GNUPGHOME\n+. \"$TEST_DIRECTORY\"/lib-gpg.sh\n+. \"$TEST_DIRECTORY\"/lib-terminal.sh\n+\n+# Mon Jul 3 23:18:43 2006 +0000\n+datestamp=1151968723\n+setdate_and_increment () {\n+    GIT_COMMITTER_DATE=\"$datestamp +0200\"\n+    datestamp=$(expr \"$datestamp\" + 1)\n+    GIT_AUTHOR_DATE=\"$datestamp +0200\"\n+    datestamp=$(expr \"$datestamp\" + 1)\n+    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n+}\n+\n+test_object_file_size () {\n+\toid=$(git rev-parse \"$1\")\n+\tpath=\".git/objects/$(test_oid_to_path $oid)\"\n+\ttest_file_size \"$path\"\n+}\n+\n+test_expect_success setup '\n+\t# setup .mailmap\n+\tcat >.mailmap <<-EOF &&\n+\tA Thor <athor@example.com> A U Thor <author@example.com>\n+\tC Mitter <cmitter@example.com> C O Mitter <committer@example.com>\n+\tEOF\n+\n+\tsetdate_and_increment &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Initial\" &&\n+\tgit branch -M main &&\n+\tsetdate_and_increment &&\n+\tgit tag -a -m \"Tagging at $datestamp\" testtag &&\n+\tgit update-ref refs/remotes/origin/main main &&\n+\tgit remote add origin nowhere &&\n+\tgit config branch.main.remote origin &&\n+\tgit config branch.main.merge refs/heads/main &&\n+\tgit remote add myfork elsewhere &&\n+\tgit config remote.pushdefault myfork &&\n+\tgit config push.default current\n+'\n+\n+test_atom () {\n+\tcase \"$1\" in\n+\t\thead) ref=refs/heads/main ;;\n+\t\t tag) ref=refs/tags/testtag ;;\n+\t\t sym) ref=refs/heads/sym ;;\n+\t\t   *) ref=$1 ;;\n+\tesac\n+\tformat=$2\n+\ttest_do=test_expect_${4:-success}\n+\n+\tprintf '%s\\n' \"$3\" >expected\n+\t$test_do $PREREQ \"basic atom: $ref $format\" '\n+\t\t${git_for_each_ref} --format=\"%($format)\" \"$ref\" >actual &&\n+\t\tsanitize_pgp <actual >actual.clean &&\n+\t\ttest_cmp expected actual.clean\n+\t'\n+\n+\t# Automatically test \"contents:size\" atom after testing \"contents\"\n+\tif test \"$format\" = \"contents\"\n+\tthen\n+\t\t# for commit leg, $3 is changed there\n+\t\texpect=$(printf '%s' \"$3\" | wc -c)\n+\t\t$test_do $PREREQ \"basic atom: $ref contents:size\" '\n+\t\t\ttype=$(git cat-file -t \"$ref\") &&\n+\t\t\tcase $type in\n+\t\t\ttag)\n+\t\t\t\t# We cannot use $3 as it expects sanitize_pgp to run\n+\t\t\t\tgit cat-file tag $ref >out &&\n+\t\t\t\texpect=$(tail -n +6 out | wc -c) &&\n+\t\t\t\trm -f out ;;\n+\t\t\ttree | blob)\n+\t\t\t\texpect=\"\" ;;\n+\t\t\tcommit)\n+\t\t\t\t: \"use the calculated expect\" ;;\n+\t\t\t*)\n+\t\t\t\tBUG \"unknown object type\" ;;\n+\t\t\tesac &&\n+\t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n+\t\t\techo $expect >expected &&\n+\t\t\t${git_for_each_ref} --format=\"%(contents:size)\" \"$ref\" >actual &&\n+\t\t\ttest_cmp expected actual\n+\t\t'\n+\tfi\n+}\n+\n+hexlen=$(test_oid hexsz)\n+\n+test_atom head refname refs/heads/main\n+test_atom head refname: refs/heads/main\n+test_atom head refname:short main\n+test_atom head refname:lstrip=1 heads/main\n+test_atom head refname:lstrip=2 main\n+test_atom head refname:lstrip=-1 main\n+test_atom head refname:lstrip=-2 heads/main\n+test_atom head refname:rstrip=1 refs/heads\n+test_atom head refname:rstrip=2 refs\n+test_atom head refname:rstrip=-1 refs\n+test_atom head refname:rstrip=-2 refs/heads\n+test_atom head refname:strip=1 heads/main\n+test_atom head refname:strip=2 main\n+test_atom head refname:strip=-1 main\n+test_atom head refname:strip=-2 heads/main\n+test_atom head upstream refs/remotes/origin/main\n+test_atom head upstream:short origin/main\n+test_atom head upstream:lstrip=2 origin/main\n+test_atom head upstream:lstrip=-2 origin/main\n+test_atom head upstream:rstrip=2 refs/remotes\n+test_atom head upstream:rstrip=-2 refs/remotes\n+test_atom head upstream:strip=2 origin/main\n+test_atom head upstream:strip=-2 origin/main\n+test_atom head push refs/remotes/myfork/main\n+test_atom head push:short myfork/main\n+test_atom head push:lstrip=1 remotes/myfork/main\n+test_atom head push:lstrip=-1 main\n+test_atom head push:rstrip=1 refs/remotes/myfork\n+test_atom head push:rstrip=-1 refs\n+test_atom head push:strip=1 remotes/myfork/main\n+test_atom head push:strip=-1 main\n+test_atom head objecttype commit\n+test_atom head objectsize $((131 + hexlen))\n+test_atom head objectsize:disk $(test_object_file_size refs/heads/main)\n+test_atom head deltabase $ZERO_OID\n+test_atom head objectname $(git rev-parse refs/heads/main)\n+test_atom head objectname:short $(git rev-parse --short refs/heads/main)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n+test_atom head tree $(git rev-parse refs/heads/main^{tree})\n+test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})\n+test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})\n+test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})\n+test_atom head parent ''\n+test_atom head parent:short ''\n+test_atom head parent:short=1 ''\n+test_atom head parent:short=10 ''\n+test_atom head numparent 0\n+test_atom head object ''\n+test_atom head type ''\n+test_atom head raw \"$(git cat-file commit refs/heads/main)\n+\"\n+test_atom head '*objectname' ''\n+test_atom head '*objecttype' ''\n+test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n+test_atom head authorname 'A U Thor'\n+test_atom head authorname:mailmap 'A Thor'\n+test_atom head authoremail '<author@example.com>'\n+test_atom head authoremail:trim 'author@example.com'\n+test_atom head authoremail:localpart 'author'\n+test_atom head authoremail:trim,localpart 'author'\n+test_atom head authoremail:mailmap '<athor@example.com>'\n+test_atom head authoremail:mailmap,trim 'athor@example.com'\n+test_atom head authoremail:trim,mailmap 'athor@example.com'\n+test_atom head authoremail:mailmap,localpart 'athor'\n+test_atom head authoremail:localpart,mailmap 'athor'\n+test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'\n+test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n+test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n+test_atom head committername 'C O Mitter'\n+test_atom head committername:mailmap 'C Mitter'\n+test_atom head committeremail '<committer@example.com>'\n+test_atom head committeremail:trim 'committer@example.com'\n+test_atom head committeremail:localpart 'committer'\n+test_atom head committeremail:localpart,trim 'committer'\n+test_atom head committeremail:mailmap '<cmitter@example.com>'\n+test_atom head committeremail:mailmap,trim 'cmitter@example.com'\n+test_atom head committeremail:trim,mailmap 'cmitter@example.com'\n+test_atom head committeremail:mailmap,localpart 'cmitter'\n+test_atom head committeremail:localpart,mailmap 'cmitter'\n+test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'\n+test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n+test_atom head tag ''\n+test_atom head tagger ''\n+test_atom head taggername ''\n+test_atom head taggeremail ''\n+test_atom head taggeremail:trim ''\n+test_atom head taggeremail:localpart ''\n+test_atom head taggerdate ''\n+test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n+test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n+test_atom head subject 'Initial'\n+test_atom head subject:sanitize 'Initial'\n+test_atom head contents:subject 'Initial'\n+test_atom head body ''\n+test_atom head contents:body ''\n+test_atom head contents:signature ''\n+test_atom head contents 'Initial\n+'\n+test_atom head HEAD '*'\n+\n+test_atom tag refname refs/tags/testtag\n+test_atom tag refname:short testtag\n+test_atom tag upstream ''\n+test_atom tag push ''\n+test_atom tag objecttype tag\n+test_atom tag objectsize $((114 + hexlen))\n+test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)\n+test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)\n+test_atom tag deltabase $ZERO_OID\n+test_atom tag '*deltabase' $ZERO_OID\n+test_atom tag objectname $(git rev-parse refs/tags/testtag)\n+test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n+test_atom tag tree ''\n+test_atom tag tree:short ''\n+test_atom tag tree:short=1 ''\n+test_atom tag tree:short=10 ''\n+test_atom tag parent ''\n+test_atom tag parent:short ''\n+test_atom tag parent:short=1 ''\n+test_atom tag parent:short=10 ''\n+test_atom tag numparent ''\n+test_atom tag object $(git rev-parse refs/tags/testtag^0)\n+test_atom tag type 'commit'\n+test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})\n+test_atom tag '*objecttype' 'commit'\n+test_atom tag author ''\n+test_atom tag authorname ''\n+test_atom tag authorname:mailmap ''\n+test_atom tag authoremail ''\n+test_atom tag authoremail:trim ''\n+test_atom tag authoremail:localpart ''\n+test_atom tag authoremail:trim,localpart ''\n+test_atom tag authoremail:mailmap ''\n+test_atom tag authoremail:mailmap,trim ''\n+test_atom tag authoremail:trim,mailmap ''\n+test_atom tag authoremail:mailmap,localpart ''\n+test_atom tag authoremail:localpart,mailmap ''\n+test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''\n+test_atom tag authordate ''\n+test_atom tag committer ''\n+test_atom tag committername ''\n+test_atom tag committername:mailmap ''\n+test_atom tag committeremail ''\n+test_atom tag committeremail:trim ''\n+test_atom tag committeremail:localpart ''\n+test_atom tag committeremail:localpart,trim ''\n+test_atom tag committeremail:mailmap ''\n+test_atom tag committeremail:mailmap,trim ''\n+test_atom tag committeremail:trim,mailmap ''\n+test_atom tag committeremail:mailmap,localpart ''\n+test_atom tag committeremail:localpart,mailmap ''\n+test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''\n+test_atom tag committerdate ''\n+test_atom tag tag 'testtag'\n+test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n+test_atom tag taggername 'C O Mitter'\n+test_atom tag taggername:mailmap 'C Mitter'\n+test_atom tag taggeremail '<committer@example.com>'\n+test_atom tag taggeremail:trim 'committer@example.com'\n+test_atom tag taggeremail:localpart 'committer'\n+test_atom tag taggeremail:trim,localpart 'committer'\n+test_atom tag taggeremail:mailmap '<cmitter@example.com>'\n+test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'\n+test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'\n+test_atom tag taggeremail:mailmap,localpart 'cmitter'\n+test_atom tag taggeremail:localpart,mailmap 'cmitter'\n+test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'\n+test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n+test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n+test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n+test_atom tag subject 'Tagging at 1151968727'\n+test_atom tag subject:sanitize 'Tagging-at-1151968727'\n+test_atom tag contents:subject 'Tagging at 1151968727'\n+test_atom tag body ''\n+test_atom tag contents:body ''\n+test_atom tag contents:signature ''\n+test_atom tag contents 'Tagging at 1151968727\n+'\n+test_atom tag HEAD ' '\n+\n+test_expect_success 'basic atom: refs/tags/testtag *raw' '\n+\tgit cat-file commit refs/tags/testtag^{} >expected &&\n+\t${git_for_each_ref} --format=\"%(*raw)\" refs/tags/testtag >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_expect_success 'Check invalid atoms names are errors' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(INVALID)\" refs/heads\n+'\n+\n+test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n+\t${git_for_each_ref} --format=\"%(authordate)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n+'\n+\n+test_expect_success 'Check valid format specifiers for date fields' '\n+\t${git_for_each_ref} --format=\"%(authordate:default)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:relative)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:short)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:local)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:iso8601)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:rfc2822)\" refs/heads\n+'\n+\n+test_expect_success 'Check invalid format specifiers are errors' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(authordate:INVALID)\" refs/heads\n+'\n+\n+test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=0)\" &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=-1)\" &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=foo)\"\n+'\n+\n+test_bad_atom () {\n+\tcase \"$1\" in\n+\thead) ref=refs/heads/main ;;\n+\t tag) ref=refs/tags/testtag ;;\n+\t sym) ref=refs/heads/sym ;;\n+\t   *) ref=$1 ;;\n+\tesac\n+\tformat=$2\n+\ttest_do=test_expect_${4:-success}\n+\n+\tprintf '%s\\n' \"$3\" >expect\n+\t$test_do $PREREQ \"err basic atom: $ref $format\" '\n+\t\ttest_must_fail ${git_for_each_ref} \\\n+\t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n+\t\ttest_cmp expect error\n+\t'\n+}\n+\n+test_bad_atom head 'authoremail:foo' \\\n+\t'fatal: unrecognized %(authoremail) argument: foo'\n+\n+test_bad_atom head 'authoremail:mailmap,trim,bar' \\\n+\t'fatal: unrecognized %(authoremail) argument: bar'\n+\n+test_bad_atom head 'authoremail:trim,' \\\n+\t'fatal: unrecognized %(authoremail) argument: '\n+\n+test_bad_atom head 'authoremail:mailmaptrim' \\\n+\t'fatal: unrecognized %(authoremail) argument: trim'\n+\n+test_bad_atom head 'committeremail: ' \\\n+\t'fatal: unrecognized %(committeremail) argument:  '\n+\n+test_bad_atom head 'committeremail: trim,foo' \\\n+\t'fatal: unrecognized %(committeremail) argument:  trim,foo'\n+\n+test_bad_atom head 'committeremail:mailmap,localpart ' \\\n+\t'fatal: unrecognized %(committeremail) argument:  '\n+\n+test_bad_atom head 'committeremail:trim_localpart' \\\n+\t'fatal: unrecognized %(committeremail) argument: _localpart'\n+\n+test_bad_atom head 'committeremail:localpart,,,trim' \\\n+\t'fatal: unrecognized %(committeremail) argument: ,,trim'\n+\n+test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \\\n+\t'fatal: unrecognized %(taggeremail) argument:  foo '\n+\n+test_bad_atom tag 'taggeremail:trim,localpart,' \\\n+\t'fatal: unrecognized %(taggeremail) argument: '\n+\n+test_bad_atom tag 'taggeremail:mailmap;localpart trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument: ;localpart trim'\n+\n+test_bad_atom tag 'taggeremail:localpart trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument:  trim'\n+\n+test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'\n+\n+test_date () {\n+\tf=$1 &&\n+\tcommitter_date=$2 &&\n+\tauthor_date=$3 &&\n+\ttagger_date=$4 &&\n+\tcat >expected <<-EOF &&\n+\t'refs/heads/main' '$committer_date' '$author_date'\n+\t'refs/tags/testtag' '$tagger_date'\n+\tEOF\n+\t(\n+\t\t${git_for_each_ref} --shell \\\n+\t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n+\t\t\trefs/heads &&\n+\t\t${git_for_each_ref} --shell \\\n+\t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n+\t\t\trefs/tags\n+\t) >actual &&\n+\ttest_cmp expected actual\n+}\n+\n+test_expect_success 'Check unformatted date fields output' '\n+\ttest_date \"\" \\\n+\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n+'\n+\n+test_expect_success 'Check format \"default\" formatted date fields output' '\n+\ttest_date default \\\n+\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n+'\n+\n+test_expect_success 'Check format \"default-local\" date fields output' '\n+\ttest_date default-local \"Mon Jul 3 23:18:43 2006\" \"Mon Jul 3 23:18:44 2006\" \"Mon Jul 3 23:18:45 2006\"\n+'\n+\n+# Don't know how to do relative check because I can't know when this script\n+# is going to be run and can't fake the current time to git, and hence can't\n+# provide expected output.  Instead, I'll just make sure that \"relative\"\n+# doesn't exit in error\n+test_expect_success 'Check format \"relative\" date fields output' '\n+\tf=relative &&\n+\t(${git_for_each_ref} --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n+\t${git_for_each_ref} --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n+'\n+\n+# We just check that this is the same as \"relative\" for now.\n+test_expect_success 'Check format \"relative-local\" date fields output' '\n+\ttest_date relative-local \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(committerdate:relative)\" refs/heads)\" \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(authordate:relative)\" refs/heads)\" \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(taggerdate:relative)\" refs/tags)\"\n+'\n+\n+test_expect_success 'Check format \"short\" date fields output' '\n+\ttest_date short 2006-07-04 2006-07-04 2006-07-04\n+'\n+\n+test_expect_success 'Check format \"short-local\" date fields output' '\n+\ttest_date short-local 2006-07-03 2006-07-03 2006-07-03\n+'\n+\n+test_expect_success 'Check format \"local\" date fields output' '\n+\ttest_date local \\\n+\t\t\"Mon Jul 3 23:18:43 2006\" \\\n+\t\t\"Mon Jul 3 23:18:44 2006\" \\\n+\t\t\"Mon Jul 3 23:18:45 2006\"\n+'\n+\n+test_expect_success 'Check format \"iso8601\" date fields output' '\n+\ttest_date iso8601 \\\n+\t\t\"2006-07-04 01:18:43 +0200\" \\\n+\t\t\"2006-07-04 01:18:44 +0200\" \\\n+\t\t\"2006-07-04 01:18:45 +0200\"\n+'\n+\n+test_expect_success 'Check format \"iso8601-local\" date fields output' '\n+\ttest_date iso8601-local \"2006-07-03 23:18:43 +0000\" \"2006-07-03 23:18:44 +0000\" \"2006-07-03 23:18:45 +0000\"\n+'\n+\n+test_expect_success 'Check format \"rfc2822\" date fields output' '\n+\ttest_date rfc2822 \\\n+\t\t\"Tue, 4 Jul 2006 01:18:43 +0200\" \\\n+\t\t\"Tue, 4 Jul 2006 01:18:44 +0200\" \\\n+\t\t\"Tue, 4 Jul 2006 01:18:45 +0200\"\n+'\n+\n+test_expect_success 'Check format \"rfc2822-local\" date fields output' '\n+\ttest_date rfc2822-local \"Mon, 3 Jul 2006 23:18:43 +0000\" \"Mon, 3 Jul 2006 23:18:44 +0000\" \"Mon, 3 Jul 2006 23:18:45 +0000\"\n+'\n+\n+test_expect_success 'Check format \"raw\" date fields output' '\n+\ttest_date raw \"1151968723 +0200\" \"1151968724 +0200\" \"1151968725 +0200\"\n+'\n+\n+test_expect_success 'Check format \"raw-local\" date fields output' '\n+\ttest_date raw-local \"1151968723 +0000\" \"1151968724 +0000\" \"1151968725 +0000\"\n+'\n+\n+test_expect_success 'Check format of strftime date fields' '\n+\techo \"my date is 2006-07-04\" >expected &&\n+\t${git_for_each_ref} \\\n+\t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n+\t  refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check format of strftime-local date fields' '\n+\techo \"my date is 2006-07-03\" >expected &&\n+\t${git_for_each_ref} \\\n+\t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n+\t  refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'exercise strftime with odd fields' '\n+\techo >expected &&\n+\t${git_for_each_ref} --format=\"%(authordate:format:)\" refs/heads >actual &&\n+\ttest_cmp expected actual &&\n+\tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n+\techo $long >expected &&\n+\t${git_for_each_ref} --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/heads/main\n+refs/remotes/origin/main\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'Verify ascending sort' '\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=refname >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/remotes/origin/main\n+refs/heads/main\n+EOF\n+\n+test_expect_success 'Verify descending sort' '\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=-refname >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Give help even with invalid sort atoms' '\n+\ttest_expect_code 129 ${git_for_each_ref} --sort=bogus -h >actual 2>&1 &&\n+\tgrep \"^usage: ${git_for_each_ref}\" actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/tags/testtag-2\n+EOF\n+\n+test_expect_success 'exercise patterns with prefixes' '\n+\tgit tag testtag-2 &&\n+\ttest_when_finished \"git tag -d testtag-2\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/tags/testtag-2\n+EOF\n+\n+test_expect_success 'exercise glob patterns with prefixes' '\n+\tgit tag testtag-2 &&\n+\ttest_when_finished \"git tag -d testtag-2\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/bar\n+refs/tags/baz\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'exercise patterns with prefix exclusions' '\n+\tfor tag in foo/one foo/two foo/three bar baz\n+\tdo\n+\t\tgit tag \"$tag\" || return 1\n+\tdone &&\n+\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/bar\n+refs/tags/baz\n+refs/tags/foo/one\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'exercise patterns with pattern exclusions' '\n+\tfor tag in foo/one foo/two foo/three bar baz\n+\tdo\n+\t\tgit tag \"$tag\" || return 1\n+\tdone &&\n+\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+'refs/heads/main'\n+'refs/remotes/origin/main'\n+'refs/tags/testtag'\n+EOF\n+\n+test_expect_success 'Quoting style: shell' '\n+\t${git_for_each_ref} --shell --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Quoting style: perl' '\n+\t${git_for_each_ref} --perl --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Quoting style: python' '\n+\t${git_for_each_ref} --python --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+\"refs/heads/main\"\n+\"refs/remotes/origin/main\"\n+\"refs/tags/testtag\"\n+EOF\n+\n+test_expect_success 'Quoting style: tcl' '\n+\t${git_for_each_ref} --tcl --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n+\ttest_expect_success \"more than one quoting style: $i\" \"\n+\t\ttest_must_fail ${git_for_each_ref} $i 2>err &&\n+\t\tgrep '^error: more than one quoting style' err\n+\t\"\n+done\n+\n+test_expect_success 'setup for upstream:track[short]' '\n+\ttest_commit two\n+'\n+\n+test_atom head upstream:track '[ahead 1]'\n+test_atom head upstream:trackshort '>'\n+test_atom head upstream:track,nobracket 'ahead 1'\n+test_atom head upstream:nobracket,track 'ahead 1'\n+\n+test_expect_success 'setup for push:track[short]' '\n+\ttest_commit third &&\n+\tgit update-ref refs/remotes/myfork/main main &&\n+\tgit reset main~1\n+'\n+\n+test_atom head push:track '[behind 1]'\n+test_atom head push:trackshort '<'\n+\n+test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:track)\" 2>/dev/null &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:trackshort)\" 2>/dev/null\n+'\n+\n+test_expect_success 'Check that :track[short] works when upstream is invalid' '\n+\tcat >expected <<-\\EOF &&\n+\t[gone]\n+\n+\tEOF\n+\ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n+\tgit config branch.main.merge refs/heads/does-not-exist &&\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n+\t\trefs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check for invalid refname format' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:INVALID)\"\n+'\n+\n+test_expect_success 'set up color tests' '\n+\tcat >expected.color <<-EOF &&\n+\t$(git rev-parse --short refs/heads/main) <GREEN>main<RESET>\n+\t$(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>\n+\t$(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>\n+\t$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>\n+\t$(git rev-parse --short refs/tags/third) <GREEN>third<RESET>\n+\t$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>\n+\tEOF\n+\tsed \"s/<[^>]*>//g\" <expected.color >expected.bare &&\n+\tcolor_format=\"%(objectname:short) %(color:green)%(refname:short)\"\n+'\n+\n+test_expect_success TTY '%(color) shows color with a tty' '\n+\ttest_terminal ${git_for_each_ref} --format=\"$color_format\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\ttest_cmp expected.color actual\n+'\n+\n+test_expect_success '%(color) does not show color without tty' '\n+\tTERM=vt100 ${git_for_each_ref} --format=\"$color_format\" >actual &&\n+\ttest_cmp expected.bare actual\n+'\n+\n+test_expect_success '--color can override tty check' '\n+\t${git_for_each_ref} --color --format=\"$color_format\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\ttest_cmp expected.color actual\n+'\n+\n+test_expect_success 'color.ui=always does not override tty check' '\n+\tgit -c color.ui=always ${git_for_each_ref#git} --format=\"$color_format\" >actual &&\n+\ttest_cmp expected.bare actual\n+'\n+\n+test_expect_success 'setup for describe atom tests' '\n+\tgit init -b master describe-repo &&\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\ttest_commit --no-tag one &&\n+\t\tgit tag tagone &&\n+\n+\t\ttest_commit --no-tag two &&\n+\t\tgit tag -a -m \"tag two\" tagtwo\n+\t)\n+'\n+\n+test_expect_success 'describe atom vs git describe' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t${git_for_each_ref} --format=\"%(objectname)\" \\\n+\t\t\trefs/tags/ >obj &&\n+\t\twhile read hash\n+\t\tdo\n+\t\t\tif desc=$(git describe $hash)\n+\t\t\tthen\n+\t\t\t\t: >expect-contains-good\n+\t\t\telse\n+\t\t\t\t: >expect-contains-bad\n+\t\t\tfi &&\n+\t\t\techo \"$hash $desc\" || return 1\n+\t\tdone <obj >expect &&\n+\t\ttest_path_exists expect-contains-good &&\n+\t\ttest_path_exists expect-contains-bad &&\n+\n+\t\t${git_for_each_ref} --format=\"%(objectname) %(describe)\" \\\n+\t\t\trefs/tags/ >actual 2>err &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest_must_be_empty err\n+\t)\n+'\n+\n+test_expect_success 'describe:tags vs describe --tags' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit describe --tags >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:tags)\" \\\n+\t\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t# Case 1: We have commits between HEAD and the most\n+\t\t#\t  recent tag reachable from it\n+\t\ttest_commit --no-tag file &&\n+\t\tgit describe --abbrev=14 >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:abbrev=14)\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# Make sure the hash used is at least 14 digits long\n+\t\tsed -e \"s/^.*-g\\([0-9a-f]*\\)$/\\1/\" <actual >hexpart &&\n+\t\ttest 15 -le $(wc -c <hexpart) &&\n+\n+\t\t# Case 2: We have a tag at HEAD, describe directly gives\n+\t\t#\t  the name of the tag\n+\t\tgit tag -a -m tagged tagname &&\n+\t\tgit describe --abbrev=14 >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:abbrev=14)\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest tagname = $(cat actual)\n+\t)\n+'\n+\n+test_expect_success 'describe:match=... vs describe --match ...' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit tag -a -m \"tag foo\" tag-foo &&\n+\t\tgit describe --match \"*-foo\" >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:match=\"*-foo\")\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit tag -a -m \"tag bar\" tag-bar &&\n+\t\tgit describe --exclude \"*-bar\" >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:exclude=\"*-bar\")\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'deref with describe atom' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tcat >expect <<-\\EOF &&\n+\n+\t\ttagname\n+\t\ttagname\n+\t\ttagname\n+\n+\t\ttagtwo\n+\t\tEOF\n+\t\t${git_for_each_ref} --format=\"%(*describe)\" >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'err on bad describe atom arg' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t# The bad arg is the only arg passed to describe atom\n+\t\tcat >expect <<-\\EOF &&\n+\t\tfatal: unrecognized %(describe) argument: baz\n+\t\tEOF\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%(describe:baz)\" \\\n+\t\t\trefs/heads/master 2>actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# The bad arg is in the middle of the option string\n+\t\t# passed to the describe atom\n+\t\tcat >expect <<-\\EOF &&\n+\t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n+\t\tEOF\n+\t\ttest_must_fail ${git_for_each_ref} \\\n+\t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n+\t\t\tref/heads/master 2>actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+cat >expected <<\\EOF\n+heads/main\n+tags/main\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs (strict)' '\n+\tgit config --bool core.warnambiguousrefs true &&\n+\tgit checkout -b newtag &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Branch\" &&\n+\tsetdate_and_increment &&\n+\tgit tag -m \"Tagging at $datestamp\" main &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/main\n+main\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs (loose)' '\n+\tgit config --bool core.warnambiguousrefs false &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/ambiguous\n+ambiguous\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n+\tgit checkout main &&\n+\tgit tag ambiguous testtag^0 &&\n+\tgit branch ambiguous testtag^0 &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create tag without tagger' '\n+\tgit tag -a -m \"Broken tag\" taggerless &&\n+\tgit tag -f taggerless $(git cat-file tag taggerless |\n+\t\tsed -e \"/^tagger /d\" |\n+\t\tgit hash-object --literally --stdin -w -t tag)\n+'\n+\n+test_atom refs/tags/taggerless type 'commit'\n+test_atom refs/tags/taggerless tag 'taggerless'\n+test_atom refs/tags/taggerless tagger ''\n+test_atom refs/tags/taggerless taggername ''\n+test_atom refs/tags/taggerless taggeremail ''\n+test_atom refs/tags/taggerless taggeremail:trim ''\n+test_atom refs/tags/taggerless taggeremail:localpart ''\n+test_atom refs/tags/taggerless taggerdate ''\n+test_atom refs/tags/taggerless committer ''\n+test_atom refs/tags/taggerless committername ''\n+test_atom refs/tags/taggerless committeremail ''\n+test_atom refs/tags/taggerless committeremail:trim ''\n+test_atom refs/tags/taggerless committeremail:localpart ''\n+test_atom refs/tags/taggerless committerdate ''\n+test_atom refs/tags/taggerless subject 'Broken tag'\n+\n+test_expect_success 'an unusual tag with an incomplete line' '\n+\n+\tgit tag -m \"bogo\" bogo &&\n+\tbogo=$(git cat-file tag bogo) &&\n+\tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n+\tgit tag -f bogo \"$bogo\" &&\n+\t${git_for_each_ref} --format \"%(body)\" refs/tags/bogo\n+\n+'\n+\n+test_expect_success 'create tag with subject and body content' '\n+\tcat >>msg <<-\\EOF &&\n+\t\tthe subject line\n+\n+\t\tfirst body line\n+\t\tsecond body line\n+\tEOF\n+\tgit tag -F msg subject-body\n+'\n+test_atom refs/tags/subject-body subject 'the subject line'\n+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n+test_atom refs/tags/subject-body body 'first body line\n+second body line\n+'\n+test_atom refs/tags/subject-body contents 'the subject line\n+\n+first body line\n+second body line\n+'\n+\n+test_expect_success 'create tag with multiline subject' '\n+\tcat >msg <<-\\EOF &&\n+\t\tfirst subject line\n+\t\tsecond subject line\n+\n+\t\tfirst body line\n+\t\tsecond body line\n+\tEOF\n+\tgit tag -F msg multiline\n+'\n+test_atom refs/tags/multiline subject 'first subject line second subject line'\n+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n+test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n+test_atom refs/tags/multiline body 'first body line\n+second body line\n+'\n+test_atom refs/tags/multiline contents:body 'first body line\n+second body line\n+'\n+test_atom refs/tags/multiline contents:signature ''\n+test_atom refs/tags/multiline contents 'first subject line\n+second subject line\n+\n+first body line\n+second body line\n+'\n+\n+test_expect_success GPG 'create signed tags' '\n+\tgit tag -s -m \"\" signed-empty &&\n+\tgit tag -s -m \"subject line\" signed-short &&\n+\tcat >msg <<-\\EOF &&\n+\tsubject line\n+\n+\tbody contents\n+\tEOF\n+\tgit tag -s -F msg signed-long\n+'\n+\n+sig='-----BEGIN PGP SIGNATURE-----\n+-----END PGP SIGNATURE-----\n+'\n+\n+PREREQ=GPG\n+test_atom refs/tags/signed-empty subject ''\n+test_atom refs/tags/signed-empty subject:sanitize ''\n+test_atom refs/tags/signed-empty contents:subject ''\n+test_atom refs/tags/signed-empty body \"$sig\"\n+test_atom refs/tags/signed-empty contents:body ''\n+test_atom refs/tags/signed-empty contents:signature \"$sig\"\n+test_atom refs/tags/signed-empty contents \"$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n+\tgit cat-file tag refs/tags/signed-empty >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_atom refs/tags/signed-short subject 'subject line'\n+test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n+test_atom refs/tags/signed-short contents:subject 'subject line'\n+test_atom refs/tags/signed-short body \"$sig\"\n+test_atom refs/tags/signed-short contents:body ''\n+test_atom refs/tags/signed-short contents:signature \"$sig\"\n+test_atom refs/tags/signed-short contents \"subject line\n+$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n+\tgit cat-file tag refs/tags/signed-short >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-short >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_atom refs/tags/signed-long subject 'subject line'\n+test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n+test_atom refs/tags/signed-long contents:subject 'subject line'\n+test_atom refs/tags/signed-long body \"body contents\n+$sig\"\n+test_atom refs/tags/signed-long contents:body 'body contents\n+'\n+test_atom refs/tags/signed-long contents:signature \"$sig\"\n+test_atom refs/tags/signed-long contents \"subject line\n+\n+body contents\n+$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n+\tgit cat-file tag refs/tags/signed-long >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-long >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_expect_success 'set up refs pointing to tree and blob' '\n+\tgit update-ref refs/mytrees/first refs/heads/main^{tree} &&\n+\tgit update-ref refs/myblobs/first refs/heads/main:one\n+'\n+\n+test_atom refs/mytrees/first subject \"\"\n+test_atom refs/mytrees/first contents:subject \"\"\n+test_atom refs/mytrees/first body \"\"\n+test_atom refs/mytrees/first contents:body \"\"\n+test_atom refs/mytrees/first contents:signature \"\"\n+test_atom refs/mytrees/first contents \"\"\n+\n+test_expect_success 'basic atom: refs/mytrees/first raw' '\n+\tgit cat-file tree refs/mytrees/first >expected &&\n+\techo >>expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file -s refs/mytrees/first >expected &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_atom refs/myblobs/first subject \"\"\n+test_atom refs/myblobs/first contents:subject \"\"\n+test_atom refs/myblobs/first body \"\"\n+test_atom refs/myblobs/first contents:body \"\"\n+test_atom refs/myblobs/first contents:signature \"\"\n+test_atom refs/myblobs/first contents \"\"\n+\n+test_expect_success 'basic atom: refs/myblobs/first raw' '\n+\tgit cat-file blob refs/myblobs/first >expected &&\n+\techo >>expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/myblobs/first >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file -s refs/myblobs/first >expected &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'set up refs pointing to binary blob' '\n+\tprintf \"a\\0b\\0c\" >blob1 &&\n+\tprintf \"a\\0c\\0b\" >blob2 &&\n+\tprintf \"\\0a\\0b\\0c\" >blob3 &&\n+\tprintf \"abc\" >blob4 &&\n+\tprintf \"\\0 \\0 \\0 \" >blob5 &&\n+\tprintf \"\\0 \\0a\\0 \" >blob6 &&\n+\tprintf \"  \" >blob7 &&\n+\t>blob8 &&\n+\tobj=$(git hash-object -w blob1) &&\n+\tgit update-ref refs/myblobs/blob1 \"$obj\" &&\n+\tobj=$(git hash-object -w blob2) &&\n+\tgit update-ref refs/myblobs/blob2 \"$obj\" &&\n+\tobj=$(git hash-object -w blob3) &&\n+\tgit update-ref refs/myblobs/blob3 \"$obj\" &&\n+\tobj=$(git hash-object -w blob4) &&\n+\tgit update-ref refs/myblobs/blob4 \"$obj\" &&\n+\tobj=$(git hash-object -w blob5) &&\n+\tgit update-ref refs/myblobs/blob5 \"$obj\" &&\n+\tobj=$(git hash-object -w blob6) &&\n+\tgit update-ref refs/myblobs/blob6 \"$obj\" &&\n+\tobj=$(git hash-object -w blob7) &&\n+\tgit update-ref refs/myblobs/blob7 \"$obj\" &&\n+\tobj=$(git hash-object -w blob8) &&\n+\tgit update-ref refs/myblobs/blob8 \"$obj\"\n+'\n+\n+test_expect_success 'Verify sorts with raw' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob8\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/blob3\n+\trefs/myblobs/blob7\n+\trefs/mytrees/first\n+\trefs/myblobs/first\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob4\n+\trefs/heads/main\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=raw \\\n+\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Verify sorts with raw:size' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob8\n+\trefs/myblobs/blob7\n+\trefs/myblobs/blob4\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob3\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/first\n+\trefs/mytrees/first\n+\trefs/heads/main\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=raw:size \\\n+\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'validate raw atom with %(if:equals)' '\n+\tcat >expected <<-EOF &&\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\trefs/myblobs/blob4\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n+\t\trefs/myblobs/ refs/heads/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'validate raw atom with %(if:notequals)' '\n+\tcat >expected <<-EOF &&\n+\trefs/heads/ambiguous\n+\trefs/heads/main\n+\trefs/heads/newtag\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob3\n+\tequals\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/blob7\n+\trefs/myblobs/blob8\n+\trefs/myblobs/first\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n+\t\trefs/myblobs/ refs/heads/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'empty raw refs with %(if)' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob1 not empty\n+\trefs/myblobs/blob2 not empty\n+\trefs/myblobs/blob3 not empty\n+\trefs/myblobs/blob4 not empty\n+\trefs/myblobs/blob5 not empty\n+\trefs/myblobs/blob6 not empty\n+\trefs/myblobs/blob7 empty\n+\trefs/myblobs/blob8 empty\n+\trefs/myblobs/first not empty\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n+\t\trefs/myblobs/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '%(raw) with --python must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --python\n+'\n+\n+test_expect_success '%(raw) with --tcl must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --tcl\n+'\n+\n+test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n+\tcmp blob1 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n+\tcmp blob3 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n+\tcmp blob8 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n+\tcmp one actual &&\n+\tgit cat-file tree refs/mytrees/first > expected &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n+\tcmp expected actual\n+'\n+\n+test_expect_success '%(raw) with --shell must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --shell\n+'\n+\n+test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --sort=raw --shell\n+'\n+\n+test_expect_success '%(raw:size) with --shell' '\n+\t${git_for_each_ref} --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" --shell >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --format compare with cat-file --batch\" '\n+\tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n+\t${git_for_each_ref} --format=\"%(objectname) %(objecttype) %(objectsize)\n+%(raw)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify sorts with contents:size' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/main\n+\trefs/heads/newtag\n+\trefs/heads/ambiguous\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\t--sort=contents:size refs/heads/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'set up multiple-sort tags' '\n+\tfor when in 100000 200000\n+\tdo\n+\t\tfor email in user1 user2\n+\t\tdo\n+\t\t\tfor ref in ref1 ref2\n+\t\t\tdo\n+\t\t\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n+\t\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n+\t\t\t\tgit tag -m \"tag $ref-$when-$email\" \\\n+\t\t\t\tmulti-$ref-$when-$email || return 1\n+\t\t\tdone\n+\t\tdone\n+\tdone\n+'\n+\n+test_expect_success 'Verify sort with multiple keys' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=-refname \\\n+\t\t--sort=taggeremail \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'equivalent sorts fall back on refname' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--no-sort cancels the previous sort keys' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=-refname \\\n+\t\t--sort=taggeremail \\\n+\t\t--no-sort \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n+\tcat >expected <<-\\EOF &&\n+\trefs/tags/multi-ref1-100000-user1\n+\trefs/tags/multi-ref1-100000-user2\n+\trefs/tags/multi-ref1-200000-user1\n+\trefs/tags/multi-ref1-200000-user2\n+\trefs/tags/multi-ref2-100000-user1\n+\trefs/tags/multi-ref2-100000-user2\n+\trefs/tags/multi-ref2-200000-user1\n+\trefs/tags/multi-ref2-200000-user2\n+\tEOF\n+\n+\t# Sort the results with `sort` for a consistent comparison against\n+\t# expected\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(refname)\" \\\n+\t\t--no-sort \\\n+\t\t\"refs/tags/multi-*\" | sort >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'set up custom date sorting' '\n+\t# Dates:\n+\t# - Wed Feb 07 2024 21:34:20 +0000\n+\t# - Tue Dec 14 1999 00:05:22 +0000\n+\t# - Fri Jun 04 2021 11:26:51 +0000\n+\t# - Mon Jan 22 2007 16:44:01 GMT+0000\n+\ti=1 &&\n+\tfor when in 1707341660 945129922 1622806011 1169484241\n+\tdo\n+\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n+\t\tGIT_COMMITTER_EMAIL=\"user@example.com\" \\\n+\t\tgit tag -m \"tag $when\" custom-dates-$i &&\n+\t\ti=$(($i+1)) || return 1\n+\tdone\n+'\n+\n+test_expect_success 'sort by date defaults to full timestamp' '\n+\tcat >expected <<-\\EOF &&\n+\t945129922 refs/tags/custom-dates-2\n+\t1169484241 refs/tags/custom-dates-4\n+\t1622806011 refs/tags/custom-dates-3\n+\t1707341660 refs/tags/custom-dates-1\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(creatordate:unix) %(refname)\" \\\n+\t\t--sort=creatordate \\\n+\t\t\"refs/tags/custom-dates-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'sort by custom date format' '\n+\tcat >expected <<-\\EOF &&\n+\t00:05:22 refs/tags/custom-dates-2\n+\t11:26:51 refs/tags/custom-dates-3\n+\t16:44:01 refs/tags/custom-dates-4\n+\t21:34:20 refs/tags/custom-dates-1\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n+\t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n+\t\t\"refs/tags/custom-dates-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n+\ttest_when_finished \"git checkout main\" &&\n+\t${git_for_each_ref} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\tsed -e \"s/^\\* /  /\" actual >expect &&\n+\tgit checkout --orphan orphaned-branch &&\n+\t${git_for_each_ref} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+cat >trailers <<EOF\n+Reviewed-by: A U Thor <author@example.com>\n+Signed-off-by: A U Thor <author@example.com>\n+[ v2 updated patch description ]\n+Acked-by: A U Thor\n+  <author@example.com>\n+EOF\n+\n+unfold () {\n+\tperl -0pe 's/\\n\\s+/ /g'\n+}\n+\n+test_expect_success 'set up trailers for next test' '\n+\techo \"Some contents\" > two &&\n+\tgit add two &&\n+\tgit commit -F - <<-EOF\n+\ttrailers: this commit message has trailers\n+\n+\tSome message contents\n+\n+\t$(cat trailers)\n+\tEOF\n+'\n+\n+test_trailer_option () {\n+\tif test \"$#\" -eq 3\n+\tthen\n+\t\tprereq=\"$1\"\n+\t\tshift\n+\tfi &&\n+\ttitle=$1 option=$2\n+\tcat >expect\n+\ttest_expect_success $prereq \"$title\" '\n+\t\t${git_for_each_ref} --format=\"%($option)\" refs/heads/main >actual &&\n+\t\ttest_cmp expect actual &&\n+\t\t${git_for_each_ref} --format=\"%(contents:$option)\" refs/heads/main >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \\\n+\t'trailers:unfold' <<-EOF\n+\t$(unfold <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only) shows only \"key: value\" trailers' \\\n+\t'trailers:only' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=no,only=true) shows only \"key: value\" trailers' \\\n+\t'trailers:only=no,only=true' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=yes) shows only \"key: value\" trailers' \\\n+\t'trailers:only=yes' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=no) shows all trailers' \\\n+\t'trailers:only=no' <<-EOF\n+\t$(cat trailers)\n+\n+\tEOF\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \\\n+\t'trailers:only,unfold' <<-EOF\n+\t$(grep -v patch.description <trailers | unfold)\n+\n+\tEOF\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \\\n+\t'trailers:unfold,only' <<-EOF\n+\t$(grep -v patch.description <trailers | unfold)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) shows that trailer' \\\n+\t'trailers:key=Signed-off-by' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) is case insensitive' \\\n+\t'trailers:key=SiGned-oFf-bY' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo:) trailing colon also works' \\\n+\t'trailers:key=Signed-off-by:' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) multiple keys' \\\n+\t'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF\n+\tReviewed-by: A U Thor <author@example.com>\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=nonexistent) becomes empty' \\\n+\t'trailers:key=Shined-off-by:' <<-EOF\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \\\n+\t'trailers:key=Acked-by' <<-EOF\n+\t$(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \\\n+\t'trailers:key=Signed-Off-by,unfold' <<-EOF\n+\t$(unfold <trailers | grep Signed-off-by)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \\\n+\t'trailers:key=Signed-off-by,only=no' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\t$(grep patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \\\n+\t'trailers:key=Signed-off-by,valueonly' <<-EOF\n+\tA U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:separator) changes separator' \\\n+\t'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>\n+\tEOF\n+\n+test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \\\n+\t'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by,A U Thor <author@example.com>\n+\tSigned-off-by,A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \\\n+\t'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>\n+\tEOF\n+\n+test_expect_success 'multiple %(trailers) use their own options' '\n+\tgit tag -F - tag-with-trailers <<-\\EOF &&\n+\tbody\n+\n+\tone: foo\n+\tone: bar\n+\ttwo: baz\n+\ttwo: qux\n+\tEOF\n+\tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n+\tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n+\t${git_for_each_ref} --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n+\tcat >expect <<-\\EOF &&\n+\toneWfooXoneWbar\n+\ttwoYbazZtwoYqux\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_failing_trailer_option () {\n+\ttitle=$1 option=$2\n+\tcat >expect\n+\ttest_expect_success \"$title\" '\n+\t\t# error message cannot be checked under i18n\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%($option)\" refs/heads/main 2>actual &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \\\n+\t'trailers:unsupported' <<-\\EOF\n+\tfatal: unknown %(trailers) argument: unsupported\n+\tEOF\n+\n+test_failing_trailer_option '%(trailers:key) without value is error' \\\n+\t'trailers:key' <<-\\EOF\n+\tfatal: expected %(trailers:key=<value>)\n+\tEOF\n+\n+test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '\n+\tcat >expect <<-EOF &&\n+\tfatal: unrecognized %(contents) argument: trailersonly\n+\tEOF\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(contents:trailersonly)\" 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'basic atom: head contents:trailers' '\n+\t${git_for_each_ref} --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\t# ${git_for_each_ref} ends with a blank line\n+\tcat >expect <<-EOF &&\n+\t$(cat trailers)\n+\n+\tEOF\n+\ttest_cmp expect actual.clean\n+'\n+\n+test_expect_success 'basic atom: rest must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(rest)\" refs/heads/main\n+'\n+\n+test_expect_success 'HEAD atom does not take arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(HEAD:foo)\" 2>err &&\n+\techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'subject atom rejects unknown arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(subject:foo)\" 2>err &&\n+\techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'refname atom rejects unknown arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:foo)\" 2>err &&\n+\techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'trailer parsing not fooled by --- line' '\n+\tgit commit --allow-empty -F - <<-\\EOF &&\n+\tthis is the subject\n+\n+\tThis is the body. The message has a \"---\" line which would confuse a\n+\tmessage+patch parser. But here we know we have only a commit message,\n+\tso we get it right.\n+\n+\ttrailer: wrong\n+\t---\n+\tThis is more body.\n+\n+\ttrailer: right\n+\tEOF\n+\n+\t{\n+\t\techo \"trailer: right\" &&\n+\t\techo\n+\t} >expect &&\n+\t${git_for_each_ref} --format=\"%(trailers)\" refs/heads/main >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'Add symbolic ref for the following tests' '\n+\tgit symbolic-ref refs/heads/sym refs/heads/main\n+'\n+\n+cat >expected <<EOF\n+refs/heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref) atom' '\n+\t${git_for_each_ref} --format=\"%(symref)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:short) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:short)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+main\n+heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual &&\n+\n+\t${git_for_each_ref} --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+refs\n+refs/heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success ':remotename and :remoteref' '\n+\tgit init remote-tests &&\n+\t(\n+\t\tcd remote-tests &&\n+\t\ttest_commit initial &&\n+\t\tgit branch -M main &&\n+\t\tgit remote add from fifth.coffee:blub &&\n+\t\tgit config branch.main.remote from &&\n+\t\tgit config branch.main.merge refs/heads/stable &&\n+\t\tgit remote add to southridge.audio:repo &&\n+\t\tgit config remote.to.push \"refs/heads/*:refs/heads/pushed/*\" &&\n+\t\tgit config branch.main.pushRemote to &&\n+\t\tfor pair in \"%(upstream)=refs/remotes/from/stable\" \\\n+\t\t\t\"%(upstream:remotename)=from\" \\\n+\t\t\t\"%(upstream:remoteref)=refs/heads/stable\" \\\n+\t\t\t\"%(push)=refs/remotes/to/pushed/main\" \\\n+\t\t\t\"%(push:remotename)=to\" \\\n+\t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n+\t\tdo\n+\t\t\techo \"${pair#*=}\" >expect &&\n+\t\t\t${git_for_each_ref} --format=\"${pair%=*}\" \\\n+\t\t\t\trefs/heads/main >actual &&\n+\t\t\ttest_cmp expect actual || exit 1\n+\t\tdone &&\n+\t\tgit branch push-simple &&\n+\t\tgit config branch.push-simple.pushRemote from &&\n+\t\tactual=\"$(${git_for_each_ref} \\\n+\t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n+\t\t\trefs/heads/push-simple)\" &&\n+\t\ttest from, = \"$actual\"\n+\t)\n+'\n+\n+test_expect_success \"${git_for_each_ref} --ignore-case ignores case\" '\n+\t${git_for_each_ref} --format=\"%(refname)\" refs/heads/MAIN >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\techo refs/heads/main >expect &&\n+\t${git_for_each_ref} --format=\"%(refname)\" --ignore-case \\\n+\t\trefs/heads/MAIN >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --omit-empty works\" '\n+\t${git_for_each_ref} --format=\"%(refname)\" >actual &&\n+\ttest_line_count -gt 1 actual &&\n+\t${git_for_each_ref} --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n+\techo refs/heads/main >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --ignore-case works on multiple sort keys\" '\n+\t# name refs numerically to avoid case-insensitive filesystem conflicts\n+\tnr=0 &&\n+\tfor email in a A b B\n+\tdo\n+\t\tfor subject in a A b B\n+\t\tdo\n+\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n+\t\t\tgit tag -m \"tag $subject\" icase-$(printf %02d $nr) &&\n+\t\t\tnr=$((nr+1))||\n+\t\t\treturn 1\n+\t\tdone\n+\tdone &&\n+\t${git_for_each_ref} --ignore-case \\\n+\t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n+\t\t--sort=refname \\\n+\t\t--sort=subject \\\n+\t\t--sort=taggeremail \\\n+\t\trefs/tags/icase-* >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t<a@example.com> tag a refs/tags/icase-00\n+\t<a@example.com> tag A refs/tags/icase-01\n+\t<A@example.com> tag a refs/tags/icase-04\n+\t<A@example.com> tag A refs/tags/icase-05\n+\t<a@example.com> tag b refs/tags/icase-02\n+\t<a@example.com> tag B refs/tags/icase-03\n+\t<A@example.com> tag b refs/tags/icase-06\n+\t<A@example.com> tag B refs/tags/icase-07\n+\t<b@example.com> tag a refs/tags/icase-08\n+\t<b@example.com> tag A refs/tags/icase-09\n+\t<B@example.com> tag a refs/tags/icase-12\n+\t<B@example.com> tag A refs/tags/icase-13\n+\t<b@example.com> tag b refs/tags/icase-10\n+\t<b@example.com> tag B refs/tags/icase-11\n+\t<B@example.com> tag b refs/tags/icase-14\n+\t<B@example.com> tag B refs/tags/icase-15\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} reports broken tags\" '\n+\tgit tag -m \"good tag\" broken-tag-good HEAD &&\n+\tgit cat-file tag broken-tag-good >good &&\n+\tsed s/commit/blob/ <good >bad &&\n+\tbad=$(git hash-object -w -t tag bad) &&\n+\tgit update-ref refs/tags/broken-tag-bad $bad &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(*objectname)\" \\\n+\t\trefs/tags/broken-tag-*\n+'\n+\n+test_expect_success 'set up tag with signature and no blank lines' '\n+\tgit tag -F - fake-sig-no-blanks <<-\\EOF\n+\tthis is the subject\n+\t-----BEGIN PGP SIGNATURE-----\n+\tnot a real signature, but we just care about the\n+\tsubject/body parsing. It is important here that\n+\tthere are no blank lines in the signature.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+'\n+\n+test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'\n+test_atom refs/tags/fake-sig-no-blanks contents:body ''\n+test_atom refs/tags/fake-sig-no-blanks contents:signature \"$sig\"\n+\n+test_expect_success 'set up tag with CRLF signature' '\n+\tappend_cr <<-\\EOF |\n+\tthis is the subject\n+\t-----BEGIN PGP SIGNATURE-----\n+\n+\tnot a real signature, but we just care about\n+\tthe subject/body parsing. It is important here\n+\tthat there is a blank line separating this\n+\tfrom the signature header.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+\tgit tag -F - --cleanup=verbatim fake-sig-crlf\n+'\n+\n+test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'\n+test_atom refs/tags/fake-sig-crlf contents:body ''\n+\n+# CRLF is retained in the signature, so we have to pass our expected value\n+# through append_cr. But test_atom requires a shell string, which means command\n+# substitution, and the shell will strip trailing newlines from the output of\n+# the substitution. Hack around it by adding and then removing a dummy line.\n+sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n+sig_crlf=${sig_crlf%dummy}\n+test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n+\n+test_expect_success 'set up tag with signature and trailers' '\n+\tgit tag -F - fake-sig-trailer <<-\\EOF\n+\tthis is the subject\n+\n+\tthis is the body\n+\n+\tMy-Trailer: foo\n+\t-----BEGIN PGP SIGNATURE-----\n+\n+\tnot a real signature, but we just care about the\n+\tsubject/body/trailer parsing.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+'\n+\n+# use \"separator=\" here to suppress the terminating newline\n+test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: empty\" '\n+\t>in &&\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\t${git_for_each_ref} --format=\"%(refname)\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: fails if extra args\" '\n+\t>in &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\t--stdin refs/heads/extra <in 2>err &&\n+\tgrep \"unknown arguments supplied with --stdin\" err\n+'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: matches\" '\n+\tcat >in <<-EOF &&\n+\trefs/tags/multi*\n+\trefs/heads/amb*\n+\tEOF\n+\n+\tcat >expect <<-EOF &&\n+\trefs/heads/ambiguous\n+\trefs/tags/multi-ref1-100000-user1\n+\trefs/tags/multi-ref1-100000-user2\n+\trefs/tags/multi-ref1-200000-user1\n+\trefs/tags/multi-ref1-200000-user2\n+\trefs/tags/multi-ref2-100000-user1\n+\trefs/tags/multi-ref2-100000-user2\n+\trefs/tags/multi-ref2-200000-user1\n+\trefs/tags/multi-ref2-200000-user2\n+\trefs/tags/multiline\n+\tEOF\n+\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} with non-existing refs\" '\n+\tcat >in <<-EOF &&\n+\trefs/heads/this-ref-does-not-exist\n+\trefs/tags/bogus\n+\tEOF\n+\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\txargs ${git_for_each_ref} --format=\"%(refname)\" <in >actual &&\n+\ttest_must_be_empty actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} with nested tags\" '\n+\tgit tag -am \"Normal tag\" nested/base HEAD &&\n+\tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n+\tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n+\n+\thead_oid=\"$(git rev-parse HEAD)\" &&\n+\tbase_tag_oid=\"$(git rev-parse refs/tags/nested/base)\" &&\n+\tnest1_tag_oid=\"$(git rev-parse refs/tags/nested/nest1)\" &&\n+\tnest2_tag_oid=\"$(git rev-parse refs/tags/nested/nest2)\" &&\n+\n+\tcat >expect <<-EOF &&\n+\trefs/tags/nested/base $base_tag_oid tag $head_oid commit\n+\trefs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit\n+\trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n+\t\trefs/tags/nested/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'is-base atom with non-commits' '\n+\t${git_for_each_ref} --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n+\tgrep \"(HEAD) refs/heads/main\" out &&\n+\n+\ttest_line_count = 2 err &&\n+\tgrep \"error: object .* is a commit, not a blob\" err &&\n+\tgrep \"error: bad tag pointer to\" err\n+'\n+\n+GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+\n+test_expect_success GPG 'setup for signature atom using gpg' '\n+\tgit checkout -b signed &&\n+\n+\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n+\n+\techo \"1\" >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -S -m \"file: 1\" &&\n+\tgit tag first-signed &&\n+\n+\techo \"2\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -m \"file: 2\" &&\n+\tgit tag second-unsigned &&\n+\n+\tgit config commit.gpgSign 1 &&\n+\techo \"3\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n+\tgit tag third-unsigned &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n+\tgit tag third-signed &&\n+\n+\techo \"4\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -SB7227189 -m \"file: 4\" &&\n+\tgit tag fourth-signed &&\n+\n+\techo \"5\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n+\tgit tag fifth-unsigned &&\n+\n+\techo \"6\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ &&\n+\tgit tag fifth-signed HEAD^ &&\n+\tgit tag sixth-signed &&\n+\n+\techo \"7\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n+\tgit tag seventh-unsigned\n+'\n+\n+test_expect_success GPGSSH 'setup for signature atom using ssh' '\n+\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n+\n+\ttest_config gpg.format ssh &&\n+\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n+\techo \"8\" >file &&\n+\ttest_tick &&\n+\tgit add file &&\n+\tgit commit -S -m \"file: 8\" &&\n+\tgit tag eighth-signed-ssh\n+'\n+\n+test_expect_success GPG2 'bare signature atom' '\n+\tgit verify-commit first-signed 2>expect &&\n+\techo  >>expect &&\n+\t${git_for_each_ref} refs/tags/first-signed \\\n+\t\t--format=\"%(signature)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show good signature with custom format' '\n+\tgit verify-commit first-signed &&\n+\tcat >expect <<-\\EOF &&\n+\tG\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\t${git_for_each_ref} refs/tags/first-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n+\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n+\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n+\tcat >expect.tmpl <<-\\EOF &&\n+\tG\n+\tFINGERPRINT\n+\tprincipal with number 1\n+\tFINGERPRINT\n+\n+\tEOF\n+\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n+\t${git_for_each_ref} refs/tags/eighth-signed-ssh \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'signature atom with grade option and bad signature' '\n+\tgit cat-file commit third-signed >raw &&\n+\tsed -e \"s/^file: 3/file: 3 forged/\" raw >forged1 &&\n+\tFORGED1=$(git hash-object -w -t commit forged1) &&\n+\tgit update-ref refs/tags/third-signed \"$FORGED1\" &&\n+\ttest_must_fail git verify-commit \"$FORGED1\" &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tB\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\n+\n+\tEOF\n+\t${git_for_each_ref} refs/tags/third-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tU\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\t${git_for_each_ref} refs/tags/fourth-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with undefined trust level' '\n+\tcat >expect <<-\\EOF &&\n+\tundefined\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\t${git_for_each_ref} refs/tags/fourth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n+\tcat >expect <<-\\EOF &&\n+\tultimate\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\t${git_for_each_ref} refs/tags/sixth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show unknown signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tE\n+\t13B6F51ECDDE430D\n+\n+\n+\n+\tEOF\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" ${git_for_each_ref} \\\n+\t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show lack of signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tN\n+\n+\n+\n+\n+\tEOF\n+\t${git_for_each_ref} refs/tags/seventh-unsigned \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex ce9af79ab1..1d9809114d 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -6,2150 +6,14 @@\n test_description='for-each-ref test'\n \n . ./test-lib.sh\n-GNUPGHOME_NOT_USED=$GNUPGHOME\n-. \"$TEST_DIRECTORY\"/lib-gpg.sh\n-. \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n-# Mon Jul 3 23:18:43 2006 +0000\n-datestamp=1151968723\n-setdate_and_increment () {\n-    GIT_COMMITTER_DATE=\"$datestamp +0200\"\n-    datestamp=$(expr \"$datestamp\" + 1)\n-    GIT_AUTHOR_DATE=\"$datestamp +0200\"\n-    datestamp=$(expr \"$datestamp\" + 1)\n-    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n-}\n-\n-test_object_file_size () {\n-\toid=$(git rev-parse \"$1\")\n-\tpath=\".git/objects/$(test_oid_to_path $oid)\"\n-\ttest_file_size \"$path\"\n-}\n-\n-test_expect_success setup '\n-\t# setup .mailmap\n-\tcat >.mailmap <<-EOF &&\n-\tA Thor <athor@example.com> A U Thor <author@example.com>\n-\tC Mitter <cmitter@example.com> C O Mitter <committer@example.com>\n-\tEOF\n-\n-\tsetdate_and_increment &&\n-\techo \"Using $datestamp\" > one &&\n-\tgit add one &&\n-\tgit commit -m \"Initial\" &&\n-\tgit branch -M main &&\n-\tsetdate_and_increment &&\n-\tgit tag -a -m \"Tagging at $datestamp\" testtag &&\n-\tgit update-ref refs/remotes/origin/main main &&\n-\tgit remote add origin nowhere &&\n-\tgit config branch.main.remote origin &&\n-\tgit config branch.main.merge refs/heads/main &&\n-\tgit remote add myfork elsewhere &&\n-\tgit config remote.pushdefault myfork &&\n-\tgit config push.default current\n-'\n-\n-test_atom () {\n-\tcase \"$1\" in\n-\t\thead) ref=refs/heads/main ;;\n-\t\t tag) ref=refs/tags/testtag ;;\n-\t\t sym) ref=refs/heads/sym ;;\n-\t\t   *) ref=$1 ;;\n-\tesac\n-\tformat=$2\n-\ttest_do=test_expect_${4:-success}\n-\n-\tprintf '%s\\n' \"$3\" >expected\n-\t$test_do $PREREQ \"basic atom: $ref $format\" '\n-\t\tgit for-each-ref --format=\"%($format)\" \"$ref\" >actual &&\n-\t\tsanitize_pgp <actual >actual.clean &&\n-\t\ttest_cmp expected actual.clean\n-\t'\n-\n-\t# Automatically test \"contents:size\" atom after testing \"contents\"\n-\tif test \"$format\" = \"contents\"\n-\tthen\n-\t\t# for commit leg, $3 is changed there\n-\t\texpect=$(printf '%s' \"$3\" | wc -c)\n-\t\t$test_do $PREREQ \"basic atom: $ref contents:size\" '\n-\t\t\ttype=$(git cat-file -t \"$ref\") &&\n-\t\t\tcase $type in\n-\t\t\ttag)\n-\t\t\t\t# We cannot use $3 as it expects sanitize_pgp to run\n-\t\t\t\tgit cat-file tag $ref >out &&\n-\t\t\t\texpect=$(tail -n +6 out | wc -c) &&\n-\t\t\t\trm -f out ;;\n-\t\t\ttree | blob)\n-\t\t\t\texpect=\"\" ;;\n-\t\t\tcommit)\n-\t\t\t\t: \"use the calculated expect\" ;;\n-\t\t\t*)\n-\t\t\t\tBUG \"unknown object type\" ;;\n-\t\t\tesac &&\n-\t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n-\t\t\techo $expect >expected &&\n-\t\t\tgit for-each-ref --format=\"%(contents:size)\" \"$ref\" >actual &&\n-\t\t\ttest_cmp expected actual\n-\t\t'\n-\tfi\n-}\n-\n-hexlen=$(test_oid hexsz)\n-\n-test_atom head refname refs/heads/main\n-test_atom head refname: refs/heads/main\n-test_atom head refname:short main\n-test_atom head refname:lstrip=1 heads/main\n-test_atom head refname:lstrip=2 main\n-test_atom head refname:lstrip=-1 main\n-test_atom head refname:lstrip=-2 heads/main\n-test_atom head refname:rstrip=1 refs/heads\n-test_atom head refname:rstrip=2 refs\n-test_atom head refname:rstrip=-1 refs\n-test_atom head refname:rstrip=-2 refs/heads\n-test_atom head refname:strip=1 heads/main\n-test_atom head refname:strip=2 main\n-test_atom head refname:strip=-1 main\n-test_atom head refname:strip=-2 heads/main\n-test_atom head upstream refs/remotes/origin/main\n-test_atom head upstream:short origin/main\n-test_atom head upstream:lstrip=2 origin/main\n-test_atom head upstream:lstrip=-2 origin/main\n-test_atom head upstream:rstrip=2 refs/remotes\n-test_atom head upstream:rstrip=-2 refs/remotes\n-test_atom head upstream:strip=2 origin/main\n-test_atom head upstream:strip=-2 origin/main\n-test_atom head push refs/remotes/myfork/main\n-test_atom head push:short myfork/main\n-test_atom head push:lstrip=1 remotes/myfork/main\n-test_atom head push:lstrip=-1 main\n-test_atom head push:rstrip=1 refs/remotes/myfork\n-test_atom head push:rstrip=-1 refs\n-test_atom head push:strip=1 remotes/myfork/main\n-test_atom head push:strip=-1 main\n-test_atom head objecttype commit\n-test_atom head objectsize $((131 + hexlen))\n-test_atom head objectsize:disk $(test_object_file_size refs/heads/main)\n-test_atom head deltabase $ZERO_OID\n-test_atom head objectname $(git rev-parse refs/heads/main)\n-test_atom head objectname:short $(git rev-parse --short refs/heads/main)\n-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n-test_atom head tree $(git rev-parse refs/heads/main^{tree})\n-test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})\n-test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})\n-test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})\n-test_atom head parent ''\n-test_atom head parent:short ''\n-test_atom head parent:short=1 ''\n-test_atom head parent:short=10 ''\n-test_atom head numparent 0\n-test_atom head object ''\n-test_atom head type ''\n-test_atom head raw \"$(git cat-file commit refs/heads/main)\n-\"\n-test_atom head '*objectname' ''\n-test_atom head '*objecttype' ''\n-test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n-test_atom head authorname 'A U Thor'\n-test_atom head authorname:mailmap 'A Thor'\n-test_atom head authoremail '<author@example.com>'\n-test_atom head authoremail:trim 'author@example.com'\n-test_atom head authoremail:localpart 'author'\n-test_atom head authoremail:trim,localpart 'author'\n-test_atom head authoremail:mailmap '<athor@example.com>'\n-test_atom head authoremail:mailmap,trim 'athor@example.com'\n-test_atom head authoremail:trim,mailmap 'athor@example.com'\n-test_atom head authoremail:mailmap,localpart 'athor'\n-test_atom head authoremail:localpart,mailmap 'athor'\n-test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'\n-test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n-test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n-test_atom head committername 'C O Mitter'\n-test_atom head committername:mailmap 'C Mitter'\n-test_atom head committeremail '<committer@example.com>'\n-test_atom head committeremail:trim 'committer@example.com'\n-test_atom head committeremail:localpart 'committer'\n-test_atom head committeremail:localpart,trim 'committer'\n-test_atom head committeremail:mailmap '<cmitter@example.com>'\n-test_atom head committeremail:mailmap,trim 'cmitter@example.com'\n-test_atom head committeremail:trim,mailmap 'cmitter@example.com'\n-test_atom head committeremail:mailmap,localpart 'cmitter'\n-test_atom head committeremail:localpart,mailmap 'cmitter'\n-test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'\n-test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n-test_atom head tag ''\n-test_atom head tagger ''\n-test_atom head taggername ''\n-test_atom head taggeremail ''\n-test_atom head taggeremail:trim ''\n-test_atom head taggeremail:localpart ''\n-test_atom head taggerdate ''\n-test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n-test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n-test_atom head subject 'Initial'\n-test_atom head subject:sanitize 'Initial'\n-test_atom head contents:subject 'Initial'\n-test_atom head body ''\n-test_atom head contents:body ''\n-test_atom head contents:signature ''\n-test_atom head contents 'Initial\n-'\n-test_atom head HEAD '*'\n-\n-test_atom tag refname refs/tags/testtag\n-test_atom tag refname:short testtag\n-test_atom tag upstream ''\n-test_atom tag push ''\n-test_atom tag objecttype tag\n-test_atom tag objectsize $((114 + hexlen))\n-test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)\n-test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)\n-test_atom tag deltabase $ZERO_OID\n-test_atom tag '*deltabase' $ZERO_OID\n-test_atom tag objectname $(git rev-parse refs/tags/testtag)\n-test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n-test_atom tag tree ''\n-test_atom tag tree:short ''\n-test_atom tag tree:short=1 ''\n-test_atom tag tree:short=10 ''\n-test_atom tag parent ''\n-test_atom tag parent:short ''\n-test_atom tag parent:short=1 ''\n-test_atom tag parent:short=10 ''\n-test_atom tag numparent ''\n-test_atom tag object $(git rev-parse refs/tags/testtag^0)\n-test_atom tag type 'commit'\n-test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})\n-test_atom tag '*objecttype' 'commit'\n-test_atom tag author ''\n-test_atom tag authorname ''\n-test_atom tag authorname:mailmap ''\n-test_atom tag authoremail ''\n-test_atom tag authoremail:trim ''\n-test_atom tag authoremail:localpart ''\n-test_atom tag authoremail:trim,localpart ''\n-test_atom tag authoremail:mailmap ''\n-test_atom tag authoremail:mailmap,trim ''\n-test_atom tag authoremail:trim,mailmap ''\n-test_atom tag authoremail:mailmap,localpart ''\n-test_atom tag authoremail:localpart,mailmap ''\n-test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''\n-test_atom tag authordate ''\n-test_atom tag committer ''\n-test_atom tag committername ''\n-test_atom tag committername:mailmap ''\n-test_atom tag committeremail ''\n-test_atom tag committeremail:trim ''\n-test_atom tag committeremail:localpart ''\n-test_atom tag committeremail:localpart,trim ''\n-test_atom tag committeremail:mailmap ''\n-test_atom tag committeremail:mailmap,trim ''\n-test_atom tag committeremail:trim,mailmap ''\n-test_atom tag committeremail:mailmap,localpart ''\n-test_atom tag committeremail:localpart,mailmap ''\n-test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''\n-test_atom tag committerdate ''\n-test_atom tag tag 'testtag'\n-test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n-test_atom tag taggername 'C O Mitter'\n-test_atom tag taggername:mailmap 'C Mitter'\n-test_atom tag taggeremail '<committer@example.com>'\n-test_atom tag taggeremail:trim 'committer@example.com'\n-test_atom tag taggeremail:localpart 'committer'\n-test_atom tag taggeremail:trim,localpart 'committer'\n-test_atom tag taggeremail:mailmap '<cmitter@example.com>'\n-test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'\n-test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'\n-test_atom tag taggeremail:mailmap,localpart 'cmitter'\n-test_atom tag taggeremail:localpart,mailmap 'cmitter'\n-test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'\n-test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n-test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n-test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n-test_atom tag subject 'Tagging at 1151968727'\n-test_atom tag subject:sanitize 'Tagging-at-1151968727'\n-test_atom tag contents:subject 'Tagging at 1151968727'\n-test_atom tag body ''\n-test_atom tag contents:body ''\n-test_atom tag contents:signature ''\n-test_atom tag contents 'Tagging at 1151968727\n-'\n-test_atom tag HEAD ' '\n-\n-test_expect_success 'basic atom: refs/tags/testtag *raw' '\n-\tgit cat-file commit refs/tags/testtag^{} >expected &&\n-\tgit for-each-ref --format=\"%(*raw)\" refs/tags/testtag >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_expect_success 'Check invalid atoms names are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'for-each-ref does not crash with -h' '\n+test_expect_success \"for-each-ref does not crash with -h\" '\n \ttest_expect_code 129 git for-each-ref -h >usage &&\n \ttest_grep \"[Uu]sage: git for-each-ref \" usage &&\n \ttest_expect_code 129 nongit git for-each-ref -h >usage &&\n \ttest_grep \"[Uu]sage: git for-each-ref \" usage\n '\n \n-test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n-\tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n-'\n-\n-test_expect_success 'Check valid format specifiers for date fields' '\n-\tgit for-each-ref --format=\"%(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:relative)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:short)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:local)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:iso8601)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:rfc2822)\" refs/heads\n-'\n-\n-test_expect_success 'Check invalid format specifiers are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n-'\n-\n-test_bad_atom () {\n-\tcase \"$1\" in\n-\thead) ref=refs/heads/main ;;\n-\t tag) ref=refs/tags/testtag ;;\n-\t sym) ref=refs/heads/sym ;;\n-\t   *) ref=$1 ;;\n-\tesac\n-\tformat=$2\n-\ttest_do=test_expect_${4:-success}\n-\n-\tprintf '%s\\n' \"$3\" >expect\n-\t$test_do $PREREQ \"err basic atom: $ref $format\" '\n-\t\ttest_must_fail git for-each-ref \\\n-\t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n-\t\ttest_cmp expect error\n-\t'\n-}\n-\n-test_bad_atom head 'authoremail:foo' \\\n-\t'fatal: unrecognized %(authoremail) argument: foo'\n-\n-test_bad_atom head 'authoremail:mailmap,trim,bar' \\\n-\t'fatal: unrecognized %(authoremail) argument: bar'\n-\n-test_bad_atom head 'authoremail:trim,' \\\n-\t'fatal: unrecognized %(authoremail) argument: '\n-\n-test_bad_atom head 'authoremail:mailmaptrim' \\\n-\t'fatal: unrecognized %(authoremail) argument: trim'\n-\n-test_bad_atom head 'committeremail: ' \\\n-\t'fatal: unrecognized %(committeremail) argument:  '\n-\n-test_bad_atom head 'committeremail: trim,foo' \\\n-\t'fatal: unrecognized %(committeremail) argument:  trim,foo'\n-\n-test_bad_atom head 'committeremail:mailmap,localpart ' \\\n-\t'fatal: unrecognized %(committeremail) argument:  '\n-\n-test_bad_atom head 'committeremail:trim_localpart' \\\n-\t'fatal: unrecognized %(committeremail) argument: _localpart'\n-\n-test_bad_atom head 'committeremail:localpart,,,trim' \\\n-\t'fatal: unrecognized %(committeremail) argument: ,,trim'\n-\n-test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \\\n-\t'fatal: unrecognized %(taggeremail) argument:  foo '\n-\n-test_bad_atom tag 'taggeremail:trim,localpart,' \\\n-\t'fatal: unrecognized %(taggeremail) argument: '\n-\n-test_bad_atom tag 'taggeremail:mailmap;localpart trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument: ;localpart trim'\n-\n-test_bad_atom tag 'taggeremail:localpart trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument:  trim'\n-\n-test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'\n-\n-test_date () {\n-\tf=$1 &&\n-\tcommitter_date=$2 &&\n-\tauthor_date=$3 &&\n-\ttagger_date=$4 &&\n-\tcat >expected <<-EOF &&\n-\t'refs/heads/main' '$committer_date' '$author_date'\n-\t'refs/tags/testtag' '$tagger_date'\n-\tEOF\n-\t(\n-\t\tgit for-each-ref --shell \\\n-\t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n-\t\t\trefs/heads &&\n-\t\tgit for-each-ref --shell \\\n-\t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n-\t\t\trefs/tags\n-\t) >actual &&\n-\ttest_cmp expected actual\n-}\n-\n-test_expect_success 'Check unformatted date fields output' '\n-\ttest_date \"\" \\\n-\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n-'\n-\n-test_expect_success 'Check format \"default\" formatted date fields output' '\n-\ttest_date default \\\n-\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n-'\n-\n-test_expect_success 'Check format \"default-local\" date fields output' '\n-\ttest_date default-local \"Mon Jul 3 23:18:43 2006\" \"Mon Jul 3 23:18:44 2006\" \"Mon Jul 3 23:18:45 2006\"\n-'\n-\n-# Don't know how to do relative check because I can't know when this script\n-# is going to be run and can't fake the current time to git, and hence can't\n-# provide expected output.  Instead, I'll just make sure that \"relative\"\n-# doesn't exit in error\n-test_expect_success 'Check format \"relative\" date fields output' '\n-\tf=relative &&\n-\t(git for-each-ref --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n-\tgit for-each-ref --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n-'\n-\n-# We just check that this is the same as \"relative\" for now.\n-test_expect_success 'Check format \"relative-local\" date fields output' '\n-\ttest_date relative-local \\\n-\t\t\"$(git for-each-ref --format=\"%(committerdate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(authordate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(taggerdate:relative)\" refs/tags)\"\n-'\n-\n-test_expect_success 'Check format \"short\" date fields output' '\n-\ttest_date short 2006-07-04 2006-07-04 2006-07-04\n-'\n-\n-test_expect_success 'Check format \"short-local\" date fields output' '\n-\ttest_date short-local 2006-07-03 2006-07-03 2006-07-03\n-'\n-\n-test_expect_success 'Check format \"local\" date fields output' '\n-\ttest_date local \\\n-\t\t\"Mon Jul 3 23:18:43 2006\" \\\n-\t\t\"Mon Jul 3 23:18:44 2006\" \\\n-\t\t\"Mon Jul 3 23:18:45 2006\"\n-'\n-\n-test_expect_success 'Check format \"iso8601\" date fields output' '\n-\ttest_date iso8601 \\\n-\t\t\"2006-07-04 01:18:43 +0200\" \\\n-\t\t\"2006-07-04 01:18:44 +0200\" \\\n-\t\t\"2006-07-04 01:18:45 +0200\"\n-'\n-\n-test_expect_success 'Check format \"iso8601-local\" date fields output' '\n-\ttest_date iso8601-local \"2006-07-03 23:18:43 +0000\" \"2006-07-03 23:18:44 +0000\" \"2006-07-03 23:18:45 +0000\"\n-'\n-\n-test_expect_success 'Check format \"rfc2822\" date fields output' '\n-\ttest_date rfc2822 \\\n-\t\t\"Tue, 4 Jul 2006 01:18:43 +0200\" \\\n-\t\t\"Tue, 4 Jul 2006 01:18:44 +0200\" \\\n-\t\t\"Tue, 4 Jul 2006 01:18:45 +0200\"\n-'\n-\n-test_expect_success 'Check format \"rfc2822-local\" date fields output' '\n-\ttest_date rfc2822-local \"Mon, 3 Jul 2006 23:18:43 +0000\" \"Mon, 3 Jul 2006 23:18:44 +0000\" \"Mon, 3 Jul 2006 23:18:45 +0000\"\n-'\n-\n-test_expect_success 'Check format \"raw\" date fields output' '\n-\ttest_date raw \"1151968723 +0200\" \"1151968724 +0200\" \"1151968725 +0200\"\n-'\n-\n-test_expect_success 'Check format \"raw-local\" date fields output' '\n-\ttest_date raw-local \"1151968723 +0000\" \"1151968724 +0000\" \"1151968725 +0000\"\n-'\n-\n-test_expect_success 'Check format of strftime date fields' '\n-\techo \"my date is 2006-07-04\" >expected &&\n-\tgit for-each-ref \\\n-\t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n-\t  refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Check format of strftime-local date fields' '\n-\techo \"my date is 2006-07-03\" >expected &&\n-\tgit for-each-ref \\\n-\t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n-\t  refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'exercise strftime with odd fields' '\n-\techo >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:)\" refs/heads >actual &&\n-\ttest_cmp expected actual &&\n-\tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n-\techo $long >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/heads/main\n-refs/remotes/origin/main\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'Verify ascending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=refname >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/remotes/origin/main\n-refs/heads/main\n-EOF\n-\n-test_expect_success 'Verify descending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=-refname >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Give help even with invalid sort atoms' '\n-\ttest_expect_code 129 git for-each-ref --sort=bogus -h >actual 2>&1 &&\n-\tgrep \"^usage: git for-each-ref\" actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/tags/testtag-2\n-EOF\n-\n-test_expect_success 'exercise patterns with prefixes' '\n-\tgit tag testtag-2 &&\n-\ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/tags/testtag-2\n-EOF\n-\n-test_expect_success 'exercise glob patterns with prefixes' '\n-\tgit tag testtag-2 &&\n-\ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/bar\n-refs/tags/baz\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'exercise patterns with prefix exclusions' '\n-\tfor tag in foo/one foo/two foo/three bar baz\n-\tdo\n-\t\tgit tag \"$tag\" || return 1\n-\tdone &&\n-\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/bar\n-refs/tags/baz\n-refs/tags/foo/one\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'exercise patterns with pattern exclusions' '\n-\tfor tag in foo/one foo/two foo/three bar baz\n-\tdo\n-\t\tgit tag \"$tag\" || return 1\n-\tdone &&\n-\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-'refs/heads/main'\n-'refs/remotes/origin/main'\n-'refs/tags/testtag'\n-EOF\n-\n-test_expect_success 'Quoting style: shell' '\n-\tgit for-each-ref --shell --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Quoting style: perl' '\n-\tgit for-each-ref --perl --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Quoting style: python' '\n-\tgit for-each-ref --python --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-\"refs/heads/main\"\n-\"refs/remotes/origin/main\"\n-\"refs/tags/testtag\"\n-EOF\n-\n-test_expect_success 'Quoting style: tcl' '\n-\tgit for-each-ref --tcl --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n-\ttest_expect_success \"more than one quoting style: $i\" \"\n-\t\ttest_must_fail git for-each-ref $i 2>err &&\n-\t\tgrep '^error: more than one quoting style' err\n-\t\"\n-done\n-\n-test_expect_success 'setup for upstream:track[short]' '\n-\ttest_commit two\n-'\n-\n-test_atom head upstream:track '[ahead 1]'\n-test_atom head upstream:trackshort '>'\n-test_atom head upstream:track,nobracket 'ahead 1'\n-test_atom head upstream:nobracket,track 'ahead 1'\n-\n-test_expect_success 'setup for push:track[short]' '\n-\ttest_commit third &&\n-\tgit update-ref refs/remotes/myfork/main main &&\n-\tgit reset main~1\n-'\n-\n-test_atom head push:track '[behind 1]'\n-test_atom head push:trackshort '<'\n-\n-test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:track)\" 2>/dev/null &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:trackshort)\" 2>/dev/null\n-'\n-\n-test_expect_success 'Check that :track[short] works when upstream is invalid' '\n-\tcat >expected <<-\\EOF &&\n-\t[gone]\n-\n-\tEOF\n-\ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n-\tgit config branch.main.merge refs/heads/does-not-exist &&\n-\tgit for-each-ref \\\n-\t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n-\t\trefs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Check for invalid refname format' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n-'\n-\n-test_expect_success 'set up color tests' '\n-\tcat >expected.color <<-EOF &&\n-\t$(git rev-parse --short refs/heads/main) <GREEN>main<RESET>\n-\t$(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>\n-\t$(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>\n-\t$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>\n-\t$(git rev-parse --short refs/tags/third) <GREEN>third<RESET>\n-\t$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>\n-\tEOF\n-\tsed \"s/<[^>]*>//g\" <expected.color >expected.bare &&\n-\tcolor_format=\"%(objectname:short) %(color:green)%(refname:short)\"\n-'\n-\n-test_expect_success TTY '%(color) shows color with a tty' '\n-\ttest_terminal git for-each-ref --format=\"$color_format\" >actual.raw &&\n-\ttest_decode_color <actual.raw >actual &&\n-\ttest_cmp expected.color actual\n-'\n-\n-test_expect_success '%(color) does not show color without tty' '\n-\tTERM=vt100 git for-each-ref --format=\"$color_format\" >actual &&\n-\ttest_cmp expected.bare actual\n-'\n-\n-test_expect_success '--color can override tty check' '\n-\tgit for-each-ref --color --format=\"$color_format\" >actual.raw &&\n-\ttest_decode_color <actual.raw >actual &&\n-\ttest_cmp expected.color actual\n-'\n-\n-test_expect_success 'color.ui=always does not override tty check' '\n-\tgit -c color.ui=always for-each-ref --format=\"$color_format\" >actual &&\n-\ttest_cmp expected.bare actual\n-'\n-\n-test_expect_success 'setup for describe atom tests' '\n-\tgit init -b master describe-repo &&\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\ttest_commit --no-tag one &&\n-\t\tgit tag tagone &&\n-\n-\t\ttest_commit --no-tag two &&\n-\t\tgit tag -a -m \"tag two\" tagtwo\n-\t)\n-'\n-\n-test_expect_success 'describe atom vs git describe' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\tgit for-each-ref --format=\"%(objectname)\" \\\n-\t\t\trefs/tags/ >obj &&\n-\t\twhile read hash\n-\t\tdo\n-\t\t\tif desc=$(git describe $hash)\n-\t\t\tthen\n-\t\t\t\t: >expect-contains-good\n-\t\t\telse\n-\t\t\t\t: >expect-contains-bad\n-\t\t\tfi &&\n-\t\t\techo \"$hash $desc\" || return 1\n-\t\tdone <obj >expect &&\n-\t\ttest_path_exists expect-contains-good &&\n-\t\ttest_path_exists expect-contains-bad &&\n-\n-\t\tgit for-each-ref --format=\"%(objectname) %(describe)\" \\\n-\t\t\trefs/tags/ >actual 2>err &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest_must_be_empty err\n-\t)\n-'\n-\n-test_expect_success 'describe:tags vs describe --tags' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit describe --tags >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:tags)\" \\\n-\t\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\t# Case 1: We have commits between HEAD and the most\n-\t\t#\t  recent tag reachable from it\n-\t\ttest_commit --no-tag file &&\n-\t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual &&\n-\n-\t\t# Make sure the hash used is at least 14 digits long\n-\t\tsed -e \"s/^.*-g\\([0-9a-f]*\\)$/\\1/\" <actual >hexpart &&\n-\t\ttest 15 -le $(wc -c <hexpart) &&\n-\n-\t\t# Case 2: We have a tag at HEAD, describe directly gives\n-\t\t#\t  the name of the tag\n-\t\tgit tag -a -m tagged tagname &&\n-\t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest tagname = $(cat actual)\n-\t)\n-'\n-\n-test_expect_success 'describe:match=... vs describe --match ...' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit tag -a -m \"tag foo\" tag-foo &&\n-\t\tgit describe --match \"*-foo\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:match=\"*-foo\")\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit tag -a -m \"tag bar\" tag-bar &&\n-\t\tgit describe --exclude \"*-bar\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:exclude=\"*-bar\")\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'deref with describe atom' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tcat >expect <<-\\EOF &&\n-\n-\t\ttagname\n-\t\ttagname\n-\t\ttagname\n-\n-\t\ttagtwo\n-\t\tEOF\n-\t\tgit for-each-ref --format=\"%(*describe)\" >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'err on bad describe atom arg' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\t# The bad arg is the only arg passed to describe atom\n-\t\tcat >expect <<-\\EOF &&\n-\t\tfatal: unrecognized %(describe) argument: baz\n-\t\tEOF\n-\t\ttest_must_fail git for-each-ref --format=\"%(describe:baz)\" \\\n-\t\t\trefs/heads/master 2>actual &&\n-\t\ttest_cmp expect actual &&\n-\n-\t\t# The bad arg is in the middle of the option string\n-\t\t# passed to the describe atom\n-\t\tcat >expect <<-\\EOF &&\n-\t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n-\t\tEOF\n-\t\ttest_must_fail git for-each-ref \\\n-\t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n-\t\t\tref/heads/master 2>actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-cat >expected <<\\EOF\n-heads/main\n-tags/main\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs (strict)' '\n-\tgit config --bool core.warnambiguousrefs true &&\n-\tgit checkout -b newtag &&\n-\techo \"Using $datestamp\" > one &&\n-\tgit add one &&\n-\tgit commit -m \"Branch\" &&\n-\tsetdate_and_increment &&\n-\tgit tag -m \"Tagging at $datestamp\" main &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-heads/main\n-main\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs (loose)' '\n-\tgit config --bool core.warnambiguousrefs false &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-heads/ambiguous\n-ambiguous\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n-\tgit checkout main &&\n-\tgit tag ambiguous testtag^0 &&\n-\tgit branch ambiguous testtag^0 &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'create tag without tagger' '\n-\tgit tag -a -m \"Broken tag\" taggerless &&\n-\tgit tag -f taggerless $(git cat-file tag taggerless |\n-\t\tsed -e \"/^tagger /d\" |\n-\t\tgit hash-object --literally --stdin -w -t tag)\n-'\n-\n-test_atom refs/tags/taggerless type 'commit'\n-test_atom refs/tags/taggerless tag 'taggerless'\n-test_atom refs/tags/taggerless tagger ''\n-test_atom refs/tags/taggerless taggername ''\n-test_atom refs/tags/taggerless taggeremail ''\n-test_atom refs/tags/taggerless taggeremail:trim ''\n-test_atom refs/tags/taggerless taggeremail:localpart ''\n-test_atom refs/tags/taggerless taggerdate ''\n-test_atom refs/tags/taggerless committer ''\n-test_atom refs/tags/taggerless committername ''\n-test_atom refs/tags/taggerless committeremail ''\n-test_atom refs/tags/taggerless committeremail:trim ''\n-test_atom refs/tags/taggerless committeremail:localpart ''\n-test_atom refs/tags/taggerless committerdate ''\n-test_atom refs/tags/taggerless subject 'Broken tag'\n-\n-test_expect_success 'an unusual tag with an incomplete line' '\n-\n-\tgit tag -m \"bogo\" bogo &&\n-\tbogo=$(git cat-file tag bogo) &&\n-\tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n-\tgit tag -f bogo \"$bogo\" &&\n-\tgit for-each-ref --format \"%(body)\" refs/tags/bogo\n-\n-'\n-\n-test_expect_success 'create tag with subject and body content' '\n-\tcat >>msg <<-\\EOF &&\n-\t\tthe subject line\n-\n-\t\tfirst body line\n-\t\tsecond body line\n-\tEOF\n-\tgit tag -F msg subject-body\n-'\n-test_atom refs/tags/subject-body subject 'the subject line'\n-test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n-test_atom refs/tags/subject-body body 'first body line\n-second body line\n-'\n-test_atom refs/tags/subject-body contents 'the subject line\n-\n-first body line\n-second body line\n-'\n-\n-test_expect_success 'create tag with multiline subject' '\n-\tcat >msg <<-\\EOF &&\n-\t\tfirst subject line\n-\t\tsecond subject line\n-\n-\t\tfirst body line\n-\t\tsecond body line\n-\tEOF\n-\tgit tag -F msg multiline\n-'\n-test_atom refs/tags/multiline subject 'first subject line second subject line'\n-test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n-test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n-test_atom refs/tags/multiline body 'first body line\n-second body line\n-'\n-test_atom refs/tags/multiline contents:body 'first body line\n-second body line\n-'\n-test_atom refs/tags/multiline contents:signature ''\n-test_atom refs/tags/multiline contents 'first subject line\n-second subject line\n-\n-first body line\n-second body line\n-'\n-\n-test_expect_success GPG 'create signed tags' '\n-\tgit tag -s -m \"\" signed-empty &&\n-\tgit tag -s -m \"subject line\" signed-short &&\n-\tcat >msg <<-\\EOF &&\n-\tsubject line\n-\n-\tbody contents\n-\tEOF\n-\tgit tag -s -F msg signed-long\n-'\n-\n-sig='-----BEGIN PGP SIGNATURE-----\n------END PGP SIGNATURE-----\n-'\n-\n-PREREQ=GPG\n-test_atom refs/tags/signed-empty subject ''\n-test_atom refs/tags/signed-empty subject:sanitize ''\n-test_atom refs/tags/signed-empty contents:subject ''\n-test_atom refs/tags/signed-empty body \"$sig\"\n-test_atom refs/tags/signed-empty contents:body ''\n-test_atom refs/tags/signed-empty contents:signature \"$sig\"\n-test_atom refs/tags/signed-empty contents \"$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n-\tgit cat-file tag refs/tags/signed-empty >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_atom refs/tags/signed-short subject 'subject line'\n-test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n-test_atom refs/tags/signed-short contents:subject 'subject line'\n-test_atom refs/tags/signed-short body \"$sig\"\n-test_atom refs/tags/signed-short contents:body ''\n-test_atom refs/tags/signed-short contents:signature \"$sig\"\n-test_atom refs/tags/signed-short contents \"subject line\n-$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n-\tgit cat-file tag refs/tags/signed-short >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-short >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_atom refs/tags/signed-long subject 'subject line'\n-test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n-test_atom refs/tags/signed-long contents:subject 'subject line'\n-test_atom refs/tags/signed-long body \"body contents\n-$sig\"\n-test_atom refs/tags/signed-long contents:body 'body contents\n-'\n-test_atom refs/tags/signed-long contents:signature \"$sig\"\n-test_atom refs/tags/signed-long contents \"subject line\n-\n-body contents\n-$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n-\tgit cat-file tag refs/tags/signed-long >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-long >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_expect_success 'set up refs pointing to tree and blob' '\n-\tgit update-ref refs/mytrees/first refs/heads/main^{tree} &&\n-\tgit update-ref refs/myblobs/first refs/heads/main:one\n-'\n-\n-test_atom refs/mytrees/first subject \"\"\n-test_atom refs/mytrees/first contents:subject \"\"\n-test_atom refs/mytrees/first body \"\"\n-test_atom refs/mytrees/first contents:body \"\"\n-test_atom refs/mytrees/first contents:signature \"\"\n-test_atom refs/mytrees/first contents \"\"\n-\n-test_expect_success 'basic atom: refs/mytrees/first raw' '\n-\tgit cat-file tree refs/mytrees/first >expected &&\n-\techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual &&\n-\tgit cat-file -s refs/mytrees/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_atom refs/myblobs/first subject \"\"\n-test_atom refs/myblobs/first contents:subject \"\"\n-test_atom refs/myblobs/first body \"\"\n-test_atom refs/myblobs/first contents:body \"\"\n-test_atom refs/myblobs/first contents:signature \"\"\n-test_atom refs/myblobs/first contents \"\"\n-\n-test_expect_success 'basic atom: refs/myblobs/first raw' '\n-\tgit cat-file blob refs/myblobs/first >expected &&\n-\techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/myblobs/first >actual &&\n-\ttest_cmp expected actual &&\n-\tgit cat-file -s refs/myblobs/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'set up refs pointing to binary blob' '\n-\tprintf \"a\\0b\\0c\" >blob1 &&\n-\tprintf \"a\\0c\\0b\" >blob2 &&\n-\tprintf \"\\0a\\0b\\0c\" >blob3 &&\n-\tprintf \"abc\" >blob4 &&\n-\tprintf \"\\0 \\0 \\0 \" >blob5 &&\n-\tprintf \"\\0 \\0a\\0 \" >blob6 &&\n-\tprintf \"  \" >blob7 &&\n-\t>blob8 &&\n-\tobj=$(git hash-object -w blob1) &&\n-\tgit update-ref refs/myblobs/blob1 \"$obj\" &&\n-\tobj=$(git hash-object -w blob2) &&\n-\tgit update-ref refs/myblobs/blob2 \"$obj\" &&\n-\tobj=$(git hash-object -w blob3) &&\n-\tgit update-ref refs/myblobs/blob3 \"$obj\" &&\n-\tobj=$(git hash-object -w blob4) &&\n-\tgit update-ref refs/myblobs/blob4 \"$obj\" &&\n-\tobj=$(git hash-object -w blob5) &&\n-\tgit update-ref refs/myblobs/blob5 \"$obj\" &&\n-\tobj=$(git hash-object -w blob6) &&\n-\tgit update-ref refs/myblobs/blob6 \"$obj\" &&\n-\tobj=$(git hash-object -w blob7) &&\n-\tgit update-ref refs/myblobs/blob7 \"$obj\" &&\n-\tobj=$(git hash-object -w blob8) &&\n-\tgit update-ref refs/myblobs/blob8 \"$obj\"\n-'\n-\n-test_expect_success 'Verify sorts with raw' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob8\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/blob3\n-\trefs/myblobs/blob7\n-\trefs/mytrees/first\n-\trefs/myblobs/first\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob4\n-\trefs/heads/main\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw \\\n-\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Verify sorts with raw:size' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob8\n-\trefs/myblobs/blob7\n-\trefs/myblobs/blob4\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob3\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/first\n-\trefs/mytrees/first\n-\trefs/heads/main\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw:size \\\n-\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'validate raw atom with %(if:equals)' '\n-\tcat >expected <<-EOF &&\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\trefs/myblobs/blob4\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tEOF\n-\tgit for-each-ref --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n-\t\trefs/myblobs/ refs/heads/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'validate raw atom with %(if:notequals)' '\n-\tcat >expected <<-EOF &&\n-\trefs/heads/ambiguous\n-\trefs/heads/main\n-\trefs/heads/newtag\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob3\n-\tequals\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/blob7\n-\trefs/myblobs/blob8\n-\trefs/myblobs/first\n-\tEOF\n-\tgit for-each-ref --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n-\t\trefs/myblobs/ refs/heads/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'empty raw refs with %(if)' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob1 not empty\n-\trefs/myblobs/blob2 not empty\n-\trefs/myblobs/blob3 not empty\n-\trefs/myblobs/blob4 not empty\n-\trefs/myblobs/blob5 not empty\n-\trefs/myblobs/blob6 not empty\n-\trefs/myblobs/blob7 empty\n-\trefs/myblobs/blob8 empty\n-\trefs/myblobs/first not empty\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n-\t\trefs/myblobs/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '%(raw) with --python must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --python\n-'\n-\n-test_expect_success '%(raw) with --tcl must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --tcl\n-'\n-\n-test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n-\tcmp blob1 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n-\tcmp blob3 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n-\tcmp blob8 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n-\tcmp one actual &&\n-\tgit cat-file tree refs/mytrees/first > expected &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n-\tcmp expected actual\n-'\n-\n-test_expect_success '%(raw) with --shell must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --shell\n-'\n-\n-test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --sort=raw --shell\n-'\n-\n-test_expect_success '%(raw:size) with --shell' '\n-\tgit for-each-ref --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n-\tgit for-each-ref --format=\"%(raw:size)\" --shell >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --format compare with cat-file --batch' '\n-\tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n-\tgit for-each-ref --format=\"%(objectname) %(objecttype) %(objectsize)\n-%(raw)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'verify sorts with contents:size' '\n-\tcat >expect <<-\\EOF &&\n-\trefs/heads/main\n-\trefs/heads/newtag\n-\trefs/heads/ambiguous\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\t--sort=contents:size refs/heads/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'set up multiple-sort tags' '\n-\tfor when in 100000 200000\n-\tdo\n-\t\tfor email in user1 user2\n-\t\tdo\n-\t\t\tfor ref in ref1 ref2\n-\t\t\tdo\n-\t\t\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n-\t\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n-\t\t\t\tgit tag -m \"tag $ref-$when-$email\" \\\n-\t\t\t\tmulti-$ref-$when-$email || return 1\n-\t\t\tdone\n-\t\tdone\n-\tdone\n-'\n-\n-test_expect_success 'Verify sort with multiple keys' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=-refname \\\n-\t\t--sort=taggeremail \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'equivalent sorts fall back on refname' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '--no-sort cancels the previous sort keys' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=-refname \\\n-\t\t--sort=taggeremail \\\n-\t\t--no-sort \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n-\tcat >expected <<-\\EOF &&\n-\trefs/tags/multi-ref1-100000-user1\n-\trefs/tags/multi-ref1-100000-user2\n-\trefs/tags/multi-ref1-200000-user1\n-\trefs/tags/multi-ref1-200000-user2\n-\trefs/tags/multi-ref2-100000-user1\n-\trefs/tags/multi-ref2-100000-user2\n-\trefs/tags/multi-ref2-200000-user1\n-\trefs/tags/multi-ref2-200000-user2\n-\tEOF\n-\n-\t# Sort the results with `sort` for a consistent comparison against\n-\t# expected\n-\tgit for-each-ref \\\n-\t\t--format=\"%(refname)\" \\\n-\t\t--no-sort \\\n-\t\t\"refs/tags/multi-*\" | sort >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'set up custom date sorting' '\n-\t# Dates:\n-\t# - Wed Feb 07 2024 21:34:20 +0000\n-\t# - Tue Dec 14 1999 00:05:22 +0000\n-\t# - Fri Jun 04 2021 11:26:51 +0000\n-\t# - Mon Jan 22 2007 16:44:01 GMT+0000\n-\ti=1 &&\n-\tfor when in 1707341660 945129922 1622806011 1169484241\n-\tdo\n-\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n-\t\tGIT_COMMITTER_EMAIL=\"user@example.com\" \\\n-\t\tgit tag -m \"tag $when\" custom-dates-$i &&\n-\t\ti=$(($i+1)) || return 1\n-\tdone\n-'\n-\n-test_expect_success 'sort by date defaults to full timestamp' '\n-\tcat >expected <<-\\EOF &&\n-\t945129922 refs/tags/custom-dates-2\n-\t1169484241 refs/tags/custom-dates-4\n-\t1622806011 refs/tags/custom-dates-3\n-\t1707341660 refs/tags/custom-dates-1\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(creatordate:unix) %(refname)\" \\\n-\t\t--sort=creatordate \\\n-\t\t\"refs/tags/custom-dates-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'sort by custom date format' '\n-\tcat >expected <<-\\EOF &&\n-\t00:05:22 refs/tags/custom-dates-2\n-\t11:26:51 refs/tags/custom-dates-3\n-\t16:44:01 refs/tags/custom-dates-4\n-\t21:34:20 refs/tags/custom-dates-1\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n-\t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n-\t\t\"refs/tags/custom-dates-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n-\ttest_when_finished \"git checkout main\" &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n-\tsed -e \"s/^\\* /  /\" actual >expect &&\n-\tgit checkout --orphan orphaned-branch &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-cat >trailers <<EOF\n-Reviewed-by: A U Thor <author@example.com>\n-Signed-off-by: A U Thor <author@example.com>\n-[ v2 updated patch description ]\n-Acked-by: A U Thor\n-  <author@example.com>\n-EOF\n-\n-unfold () {\n-\tperl -0pe 's/\\n\\s+/ /g'\n-}\n-\n-test_expect_success 'set up trailers for next test' '\n-\techo \"Some contents\" > two &&\n-\tgit add two &&\n-\tgit commit -F - <<-EOF\n-\ttrailers: this commit message has trailers\n-\n-\tSome message contents\n-\n-\t$(cat trailers)\n-\tEOF\n-'\n-\n-test_trailer_option () {\n-\tif test \"$#\" -eq 3\n-\tthen\n-\t\tprereq=\"$1\"\n-\t\tshift\n-\tfi &&\n-\ttitle=$1 option=$2\n-\tcat >expect\n-\ttest_expect_success $prereq \"$title\" '\n-\t\tgit for-each-ref --format=\"%($option)\" refs/heads/main >actual &&\n-\t\ttest_cmp expect actual &&\n-\t\tgit for-each-ref --format=\"%(contents:$option)\" refs/heads/main >actual &&\n-\t\ttest_cmp expect actual\n-\t'\n-}\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \\\n-\t'trailers:unfold' <<-EOF\n-\t$(unfold <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only) shows only \"key: value\" trailers' \\\n-\t'trailers:only' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=no,only=true) shows only \"key: value\" trailers' \\\n-\t'trailers:only=no,only=true' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=yes) shows only \"key: value\" trailers' \\\n-\t'trailers:only=yes' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=no) shows all trailers' \\\n-\t'trailers:only=no' <<-EOF\n-\t$(cat trailers)\n-\n-\tEOF\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \\\n-\t'trailers:only,unfold' <<-EOF\n-\t$(grep -v patch.description <trailers | unfold)\n-\n-\tEOF\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \\\n-\t'trailers:unfold,only' <<-EOF\n-\t$(grep -v patch.description <trailers | unfold)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) shows that trailer' \\\n-\t'trailers:key=Signed-off-by' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) is case insensitive' \\\n-\t'trailers:key=SiGned-oFf-bY' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo:) trailing colon also works' \\\n-\t'trailers:key=Signed-off-by:' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) multiple keys' \\\n-\t'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF\n-\tReviewed-by: A U Thor <author@example.com>\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=nonexistent) becomes empty' \\\n-\t'trailers:key=Shined-off-by:' <<-EOF\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \\\n-\t'trailers:key=Acked-by' <<-EOF\n-\t$(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \\\n-\t'trailers:key=Signed-Off-by,unfold' <<-EOF\n-\t$(unfold <trailers | grep Signed-off-by)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \\\n-\t'trailers:key=Signed-off-by,only=no' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\t$(grep patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \\\n-\t'trailers:key=Signed-off-by,valueonly' <<-EOF\n-\tA U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:separator) changes separator' \\\n-\t'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>\n-\tEOF\n-\n-test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \\\n-\t'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by,A U Thor <author@example.com>\n-\tSigned-off-by,A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \\\n-\t'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>\n-\tEOF\n-\n-test_expect_success 'multiple %(trailers) use their own options' '\n-\tgit tag -F - tag-with-trailers <<-\\EOF &&\n-\tbody\n-\n-\tone: foo\n-\tone: bar\n-\ttwo: baz\n-\ttwo: qux\n-\tEOF\n-\tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n-\tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n-\tgit for-each-ref --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n-\tcat >expect <<-\\EOF &&\n-\toneWfooXoneWbar\n-\ttwoYbazZtwoYqux\n-\tEOF\n-\ttest_cmp expect actual\n-'\n-\n-test_failing_trailer_option () {\n-\ttitle=$1 option=$2\n-\tcat >expect\n-\ttest_expect_success \"$title\" '\n-\t\t# error message cannot be checked under i18n\n-\t\ttest_must_fail git for-each-ref --format=\"%($option)\" refs/heads/main 2>actual &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest_must_fail git for-each-ref --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n-\t\ttest_cmp expect actual\n-\t'\n-}\n-\n-test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \\\n-\t'trailers:unsupported' <<-\\EOF\n-\tfatal: unknown %(trailers) argument: unsupported\n-\tEOF\n-\n-test_failing_trailer_option '%(trailers:key) without value is error' \\\n-\t'trailers:key' <<-\\EOF\n-\tfatal: expected %(trailers:key=<value>)\n-\tEOF\n-\n-test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '\n-\tcat >expect <<-EOF &&\n-\tfatal: unrecognized %(contents) argument: trailersonly\n-\tEOF\n-\ttest_must_fail git for-each-ref --format=\"%(contents:trailersonly)\" 2>actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'basic atom: head contents:trailers' '\n-\tgit for-each-ref --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\t# git for-each-ref ends with a blank line\n-\tcat >expect <<-EOF &&\n-\t$(cat trailers)\n-\n-\tEOF\n-\ttest_cmp expect actual.clean\n-'\n-\n-test_expect_success 'basic atom: rest must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(rest)\" refs/heads/main\n-'\n-\n-test_expect_success 'HEAD atom does not take arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(HEAD:foo)\" 2>err &&\n-\techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'subject atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(subject:foo)\" 2>err &&\n-\techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'refname atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:foo)\" 2>err &&\n-\techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'trailer parsing not fooled by --- line' '\n-\tgit commit --allow-empty -F - <<-\\EOF &&\n-\tthis is the subject\n-\n-\tThis is the body. The message has a \"---\" line which would confuse a\n-\tmessage+patch parser. But here we know we have only a commit message,\n-\tso we get it right.\n-\n-\ttrailer: wrong\n-\t---\n-\tThis is more body.\n-\n-\ttrailer: right\n-\tEOF\n-\n-\t{\n-\t\techo \"trailer: right\" &&\n-\t\techo\n-\t} >expect &&\n-\tgit for-each-ref --format=\"%(trailers)\" refs/heads/main >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'Add symbolic ref for the following tests' '\n-\tgit symbolic-ref refs/heads/sym refs/heads/main\n-'\n-\n-cat >expected <<EOF\n-refs/heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref) atom' '\n-\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:short) atom' '\n-\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-main\n-heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual &&\n-\n-\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-refs\n-refs/heads\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success ':remotename and :remoteref' '\n-\tgit init remote-tests &&\n-\t(\n-\t\tcd remote-tests &&\n-\t\ttest_commit initial &&\n-\t\tgit branch -M main &&\n-\t\tgit remote add from fifth.coffee:blub &&\n-\t\tgit config branch.main.remote from &&\n-\t\tgit config branch.main.merge refs/heads/stable &&\n-\t\tgit remote add to southridge.audio:repo &&\n-\t\tgit config remote.to.push \"refs/heads/*:refs/heads/pushed/*\" &&\n-\t\tgit config branch.main.pushRemote to &&\n-\t\tfor pair in \"%(upstream)=refs/remotes/from/stable\" \\\n-\t\t\t\"%(upstream:remotename)=from\" \\\n-\t\t\t\"%(upstream:remoteref)=refs/heads/stable\" \\\n-\t\t\t\"%(push)=refs/remotes/to/pushed/main\" \\\n-\t\t\t\"%(push:remotename)=to\" \\\n-\t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n-\t\tdo\n-\t\t\techo \"${pair#*=}\" >expect &&\n-\t\t\tgit for-each-ref --format=\"${pair%=*}\" \\\n-\t\t\t\trefs/heads/main >actual &&\n-\t\t\ttest_cmp expect actual || exit 1\n-\t\tdone &&\n-\t\tgit branch push-simple &&\n-\t\tgit config branch.push-simple.pushRemote from &&\n-\t\tactual=\"$(git for-each-ref \\\n-\t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n-\t\t\trefs/heads/push-simple)\" &&\n-\t\ttest from, = \"$actual\"\n-\t)\n-'\n-\n-test_expect_success 'for-each-ref --ignore-case ignores case' '\n-\tgit for-each-ref --format=\"%(refname)\" refs/heads/MAIN >actual &&\n-\ttest_must_be_empty actual &&\n-\n-\techo refs/heads/main >expect &&\n-\tgit for-each-ref --format=\"%(refname)\" --ignore-case \\\n-\t\trefs/heads/MAIN >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --omit-empty works' '\n-\tgit for-each-ref --format=\"%(refname)\" >actual &&\n-\ttest_line_count -gt 1 actual &&\n-\tgit for-each-ref --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n-\techo refs/heads/main >expect &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n-\t# name refs numerically to avoid case-insensitive filesystem conflicts\n-\tnr=0 &&\n-\tfor email in a A b B\n-\tdo\n-\t\tfor subject in a A b B\n-\t\tdo\n-\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n-\t\t\tgit tag -m \"tag $subject\" icase-$(printf %02d $nr) &&\n-\t\t\tnr=$((nr+1))||\n-\t\t\treturn 1\n-\t\tdone\n-\tdone &&\n-\tgit for-each-ref --ignore-case \\\n-\t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n-\t\t--sort=refname \\\n-\t\t--sort=subject \\\n-\t\t--sort=taggeremail \\\n-\t\trefs/tags/icase-* >actual &&\n-\tcat >expect <<-\\EOF &&\n-\t<a@example.com> tag a refs/tags/icase-00\n-\t<a@example.com> tag A refs/tags/icase-01\n-\t<A@example.com> tag a refs/tags/icase-04\n-\t<A@example.com> tag A refs/tags/icase-05\n-\t<a@example.com> tag b refs/tags/icase-02\n-\t<a@example.com> tag B refs/tags/icase-03\n-\t<A@example.com> tag b refs/tags/icase-06\n-\t<A@example.com> tag B refs/tags/icase-07\n-\t<b@example.com> tag a refs/tags/icase-08\n-\t<b@example.com> tag A refs/tags/icase-09\n-\t<B@example.com> tag a refs/tags/icase-12\n-\t<B@example.com> tag A refs/tags/icase-13\n-\t<b@example.com> tag b refs/tags/icase-10\n-\t<b@example.com> tag B refs/tags/icase-11\n-\t<B@example.com> tag b refs/tags/icase-14\n-\t<B@example.com> tag B refs/tags/icase-15\n-\tEOF\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref reports broken tags' '\n-\tgit tag -m \"good tag\" broken-tag-good HEAD &&\n-\tgit cat-file tag broken-tag-good >good &&\n-\tsed s/commit/blob/ <good >bad &&\n-\tbad=$(git hash-object -w -t tag bad) &&\n-\tgit update-ref refs/tags/broken-tag-bad $bad &&\n-\ttest_must_fail git for-each-ref --format=\"%(*objectname)\" \\\n-\t\trefs/tags/broken-tag-*\n-'\n-\n-test_expect_success 'set up tag with signature and no blank lines' '\n-\tgit tag -F - fake-sig-no-blanks <<-\\EOF\n-\tthis is the subject\n-\t-----BEGIN PGP SIGNATURE-----\n-\tnot a real signature, but we just care about the\n-\tsubject/body parsing. It is important here that\n-\tthere are no blank lines in the signature.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-'\n-\n-test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'\n-test_atom refs/tags/fake-sig-no-blanks contents:body ''\n-test_atom refs/tags/fake-sig-no-blanks contents:signature \"$sig\"\n-\n-test_expect_success 'set up tag with CRLF signature' '\n-\tappend_cr <<-\\EOF |\n-\tthis is the subject\n-\t-----BEGIN PGP SIGNATURE-----\n-\n-\tnot a real signature, but we just care about\n-\tthe subject/body parsing. It is important here\n-\tthat there is a blank line separating this\n-\tfrom the signature header.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-\tgit tag -F - --cleanup=verbatim fake-sig-crlf\n-'\n-\n-test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'\n-test_atom refs/tags/fake-sig-crlf contents:body ''\n-\n-# CRLF is retained in the signature, so we have to pass our expected value\n-# through append_cr. But test_atom requires a shell string, which means command\n-# substitution, and the shell will strip trailing newlines from the output of\n-# the substitution. Hack around it by adding and then removing a dummy line.\n-sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n-sig_crlf=${sig_crlf%dummy}\n-test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n-\n-test_expect_success 'set up tag with signature and trailers' '\n-\tgit tag -F - fake-sig-trailer <<-\\EOF\n-\tthis is the subject\n-\n-\tthis is the body\n-\n-\tMy-Trailer: foo\n-\t-----BEGIN PGP SIGNATURE-----\n-\n-\tnot a real signature, but we just care about the\n-\tsubject/body/trailer parsing.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-'\n-\n-# use \"separator=\" here to suppress the terminating newline\n-test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n-\n-test_expect_success 'git for-each-ref --stdin: empty' '\n-\t>in &&\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\tgit for-each-ref --format=\"%(refname)\" >expect &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'git for-each-ref --stdin: fails if extra args' '\n-\t>in &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname)\" \\\n-\t\t--stdin refs/heads/extra <in 2>err &&\n-\tgrep \"unknown arguments supplied with --stdin\" err\n-'\n-\n-test_expect_success 'git for-each-ref --stdin: matches' '\n-\tcat >in <<-EOF &&\n-\trefs/tags/multi*\n-\trefs/heads/amb*\n-\tEOF\n-\n-\tcat >expect <<-EOF &&\n-\trefs/heads/ambiguous\n-\trefs/tags/multi-ref1-100000-user1\n-\trefs/tags/multi-ref1-100000-user2\n-\trefs/tags/multi-ref1-200000-user1\n-\trefs/tags/multi-ref1-200000-user2\n-\trefs/tags/multi-ref2-100000-user1\n-\trefs/tags/multi-ref2-100000-user2\n-\trefs/tags/multi-ref2-200000-user1\n-\trefs/tags/multi-ref2-200000-user2\n-\trefs/tags/multiline\n-\tEOF\n-\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'git for-each-ref with non-existing refs' '\n-\tcat >in <<-EOF &&\n-\trefs/heads/this-ref-does-not-exist\n-\trefs/tags/bogus\n-\tEOF\n-\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\ttest_must_be_empty actual &&\n-\n-\txargs git for-each-ref --format=\"%(refname)\" <in >actual &&\n-\ttest_must_be_empty actual\n-'\n-\n-test_expect_success 'git for-each-ref with nested tags' '\n-\tgit tag -am \"Normal tag\" nested/base HEAD &&\n-\tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n-\tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n-\n-\thead_oid=\"$(git rev-parse HEAD)\" &&\n-\tbase_tag_oid=\"$(git rev-parse refs/tags/nested/base)\" &&\n-\tnest1_tag_oid=\"$(git rev-parse refs/tags/nested/nest1)\" &&\n-\tnest2_tag_oid=\"$(git rev-parse refs/tags/nested/nest2)\" &&\n-\n-\tcat >expect <<-EOF &&\n-\trefs/tags/nested/base $base_tag_oid tag $head_oid commit\n-\trefs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit\n-\trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n-\t\trefs/tags/nested/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'is-base atom with non-commits' '\n-\tgit for-each-ref --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n-\tgrep \"(HEAD) refs/heads/main\" out &&\n-\n-\ttest_line_count = 2 err &&\n-\tgrep \"error: object .* is a commit, not a blob\" err &&\n-\tgrep \"error: bad tag pointer to\" err\n-'\n-\n-GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n-TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n-\n-test_expect_success GPG 'setup for signature atom using gpg' '\n-\tgit checkout -b signed &&\n-\n-\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n-\n-\techo \"1\" >file &&\n-\tgit add file &&\n-\ttest_tick &&\n-\tgit commit -S -m \"file: 1\" &&\n-\tgit tag first-signed &&\n-\n-\techo \"2\" >file &&\n-\ttest_tick &&\n-\tgit commit -a -m \"file: 2\" &&\n-\tgit tag second-unsigned &&\n-\n-\tgit config commit.gpgSign 1 &&\n-\techo \"3\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n-\tgit tag third-unsigned &&\n-\n-\ttest_tick &&\n-\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n-\tgit tag third-signed &&\n-\n-\techo \"4\" >file &&\n-\ttest_tick &&\n-\tgit commit -a -SB7227189 -m \"file: 4\" &&\n-\tgit tag fourth-signed &&\n-\n-\techo \"5\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n-\tgit tag fifth-unsigned &&\n-\n-\techo \"6\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n-\n-\ttest_tick &&\n-\tgit rebase -f HEAD^^ &&\n-\tgit tag fifth-signed HEAD^ &&\n-\tgit tag sixth-signed &&\n-\n-\techo \"7\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n-\tgit tag seventh-unsigned\n-'\n-\n-test_expect_success GPGSSH 'setup for signature atom using ssh' '\n-\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n-\n-\ttest_config gpg.format ssh &&\n-\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n-\techo \"8\" >file &&\n-\ttest_tick &&\n-\tgit add file &&\n-\tgit commit -S -m \"file: 8\" &&\n-\tgit tag eighth-signed-ssh\n-'\n-\n-test_expect_success GPG2 'bare signature atom' '\n-\tgit verify-commit first-signed 2>expect &&\n-\techo  >>expect &&\n-\tgit for-each-ref refs/tags/first-signed \\\n-\t\t--format=\"%(signature)\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show good signature with custom format' '\n-\tgit verify-commit first-signed &&\n-\tcat >expect <<-\\EOF &&\n-\tG\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\tEOF\n-\tgit for-each-ref refs/tags/first-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n-\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n-\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n-\tcat >expect.tmpl <<-\\EOF &&\n-\tG\n-\tFINGERPRINT\n-\tprincipal with number 1\n-\tFINGERPRINT\n-\n-\tEOF\n-\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n-\tgit for-each-ref refs/tags/eighth-signed-ssh \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'signature atom with grade option and bad signature' '\n-\tgit cat-file commit third-signed >raw &&\n-\tsed -e \"s/^file: 3/file: 3 forged/\" raw >forged1 &&\n-\tFORGED1=$(git hash-object -w -t commit forged1) &&\n-\tgit update-ref refs/tags/third-signed \"$FORGED1\" &&\n-\ttest_must_fail git verify-commit \"$FORGED1\" &&\n-\n-\tcat >expect <<-\\EOF &&\n-\tB\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\n-\n-\tEOF\n-\tgit for-each-ref refs/tags/third-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tU\n-\t65A0EEA02E30CAD7\n-\tEris Discordia <discord@example.net>\n-\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n-\tD4BE22311AD3131E5EDA29A461092E85B7227189\n-\tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with undefined trust level' '\n-\tcat >expect <<-\\EOF &&\n-\tundefined\n-\t65A0EEA02E30CAD7\n-\tEris Discordia <discord@example.net>\n-\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n-\tD4BE22311AD3131E5EDA29A461092E85B7227189\n-\tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n-\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n-\tcat >expect <<-\\EOF &&\n-\tultimate\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\tEOF\n-\tgit for-each-ref refs/tags/sixth-signed \\\n-\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show unknown signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tE\n-\t13B6F51ECDDE430D\n-\n-\n-\n-\tEOF\n-\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n-\t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show lack of signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tN\n-\n-\n-\n-\n-\tEOF\n-\tgit for-each-ref refs/tags/seventh-unsigned \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n+. \"$TEST_DIRECTORY\"/for-each-ref-tests.sh\n \n test_done\n-- \n2.34.1\n\n"},{"id":"523088","messageId":"20250731090040.1625303-6-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250731090040.1625303-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v4 5/5] t: add test for git refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-07-31T09:00:40Z","receivedAt":"2025-07-31T09:01:09Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Add a test script, `t/t1461-refs-list.sh`, for the new `git refs list`\ncommand.\n\nThis script acts as a simple driver, leveraging the shared test library\ncreated in the preceding commit. It works by overriding the\n`$git_for_each_ref` variable to \"git refs list\" and then sourcing the\nshared library (`t/for-each-ref-tests.sh`).\n\nThis approach ensures that `git refs list` is tested against the\nentire comprehensive test suite of `git for-each-ref`, verifying\nthat it acts as a compatible drop-in replacement.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/meson.build        | 1 +\n t/t1461-refs-list.sh | 8 ++++++++\n 2 files changed, 9 insertions(+)\n create mode 100755 t/t1461-refs-list.sh\n\ndiff --git a/t/meson.build b/t/meson.build\nindex 50e89e764a..c959c039d0 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -224,6 +224,7 @@ integration_tests = [\n   't1450-fsck.sh',\n   't1451-fsck-buffer.sh',\n   't1460-refs-migrate.sh',\n+  't1461-refs-list.sh',\n   't1500-rev-parse.sh',\n   't1501-work-tree.sh',\n   't1502-rev-parse-parseopt.sh',\ndiff --git a/t/t1461-refs-list.sh b/t/t1461-refs-list.sh\nnew file mode 100755\nindex 0000000000..36e3d81e59\n--- /dev/null\n+++ b/t/t1461-refs-list.sh\n@@ -0,0 +1,8 @@\n+#!/bin/sh\n+\n+test_description='git refs list tests'\n+\n+. ./test-lib.sh\n+\n+git_for_each_ref='git refs list'\n+. \"$TEST_DIRECTORY\"/for-each-ref-tests.sh\n-- \n2.34.1\n\n"},{"id":"523169","messageId":"aIxWlX36V6DcxWa0@pks.im","threadId":"63703","inReplyTo":"20250731090040.1625303-3-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH v4 2/5] builtin/for-each-ref: factor out core logic into a helper","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-01T05:54:29Z","receivedAt":"2025-08-01T05:54:43Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jul 31, 2025 at 02:30:37PM +0530, Meet Soni wrote:\n> diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\n> index 3d2207ec77..bbc0e5ad1c 100644\n> --- a/builtin/for-each-ref.c\n> +++ b/builtin/for-each-ref.c\n> @@ -7,19 +7,9 @@\n>  #include \"ref-filter.h\"\n>  #include \"strbuf.h\"\n>  #include \"strvec.h\"\n> +#include \"for-each-ref.h\"\n\nLet's keep the includes alphabetically sorted.\n\n> diff --git a/t/t0450/adoc-help-mismatches b/t/t0450/adoc-help-mismatches\n> index 06b469bdee..2c6ecd5fc8 100644\n> --- a/t/t0450/adoc-help-mismatches\n> +++ b/t/t0450/adoc-help-mismatches\n> @@ -17,7 +17,6 @@ fast-export\n>  fast-import\n>  fetch-pack\n>  fmt-merge-msg\n> -for-each-ref\n>  format-patch\n>  fsck-objects\n>  fsmonitor--daemon\n\nEverything else looks sensible, but this change is surprising as it\nwasn't mentioned in the commit message. I see that you changed the usage\nthough to match what we have in the man page. That's a good change\noverall, but should probably be moved into a separate commit so that\nthis commit here is not changing any behaviour.\n\nPatrick\n"},{"id":"523170","messageId":"aIxWocMpvHJ7bL0V@pks.im","threadId":"63703","inReplyTo":"20250731090040.1625303-1-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH v4 0/5] Add refs list subcommand","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-01T05:54:41Z","receivedAt":"2025-08-01T05:54:46Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jul 31, 2025 at 02:30:35PM +0530, Meet Soni wrote:\n> Hello everyone,\n> \n> This is the fourth version of the patch series that introduces the git\n> refs list subcommand.\n> \n> Changes in v4:\n> \n>   - Implemented architectural refactoring. The core logic of\n>     for-each-ref now resides in a shared helper function, and both\n>     for-each-ref and refs list have been simplified to thin wrappers\n>     around it.\n> \n>   - The usage strings have also been refactored. The common options\n>     are now defined in a shared macro in a new for-each-ref.h header.\n> \n>   - The patch series has been further split to cleanly separate the\n>     preparatory refactoring commits (for both the C code and the\n>     AsciiDoc documentation) from the commit that introduces the new\n>     feature.\n> \n>   - As a beneficial side-effect of unifying the usage strings, this\n>     series now fixes a pre-existing inconsistency between the --help\n>     output and the man page for for-each-ref. This allows a known\n>     breakage for it in the t0450 documentation test to be removed.\n\nI've only got some minor nits that should be quick to address,\neverything else looks quite sensible to me. So I think this is almost\nready to be merged.\n\nThanks!\n\nPatrick\n"},{"id":"523203","messageId":"2d2f823d-6e85-44a0-85d2-d45d4dc287fc@gmail.com","threadId":"63703","inReplyTo":"20250731090040.1625303-4-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH v4 3/5] builtin/refs: add list subcommand","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-08-01T13:27:47Z","receivedAt":"2025-08-01T13:27:52Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Soni\n\nOn 31/07/2025 10:00, Meet Soni wrote:\n> Git's reference management is distributed across multiple commands. As\n> part of an ongoing effort to consolidate and modernize reference\n> handling, introduce a `list` subcommand under the `git refs` umbrella as\n> a replacement for `git for-each-ref`.\n> \n> Implement `cmd_refs_list` by having it call the `for_each_ref_core()`\n> helper function. This helper was factored out of the original\n> `cmd_for_each_ref` in a preceding commit, allowing both commands to\n> share the same core logic as independent peers.\n> \n> Add documentation for the new command. The man page leverages the shared\n> options file, created in a previous commit, by using the AsciiDoc\n> `include::` macro to ensure consistency with git-for-each-ref(1).\n\nI find the behavior of for-each-ref quite confusing so I wonder if we \nreally want to copy it to the new command. For example\n\n     git for-each-ref 'refs/h*'\n\ndoes not print anything but\n\n     git for-each-ref 'refs/heads/m*'\n\nprints all the branches beginning with m. Another example is\n\n     git for-each-ref 'refs/heads'\n\nprints all the branches but\n\n     git for-each-ref 'refs/heads*'\n\nprints nothing. To me it would be much easier to understand if the \npattern always required an explicit wildcard (or possible always did a \nprefix match in the absence of a wildcard).\n\nThanks\n\nPhillip\n\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Mentored-by: shejialuo <shejialuo@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Signed-off-by: Meet Soni <meetsoni3017@gmail.com>\n> ---\n>   Documentation/git-refs.adoc | 16 ++++++++++++++++\n>   builtin/refs.c              | 14 ++++++++++++++\n>   2 files changed, 30 insertions(+)\n> \n> diff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\n> index 4d6dc994f9..ee563aa7e0 100644\n> --- a/Documentation/git-refs.adoc\n> +++ b/Documentation/git-refs.adoc\n> @@ -11,6 +11,13 @@ SYNOPSIS\n>   [synopsis]\n>   git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n>   git refs verify [--strict] [--verbose]\n> +git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n> +\t      [(--sort=<key>)...] [--format=<format>]\n> +\t      [--include-root-refs] [ --stdin | <pattern>... ]\n> +\t      [--points-at=<object>]\n> +\t      [--merged[=<object>]] [--no-merged[=<object>]]\n> +\t      [--contains[=<object>]] [--no-contains[=<object>]]\n> +\t      [--exclude=<pattern> ...]\n>   \n>   DESCRIPTION\n>   -----------\n> @@ -26,6 +33,11 @@ migrate::\n>   verify::\n>   \tVerify reference database consistency.\n>   \n> +list::\n> +\tList references in the repository with support for filtering,\n> +\tformatting, and sorting. This subcommand is an alias for\n> +\tlinkgit:git-for-each-ref[1] and offers identical functionality.\n> +\n>   OPTIONS\n>   -------\n>   \n> @@ -57,6 +69,10 @@ The following options are specific to 'git refs verify':\n>   --verbose::\n>   \tWhen verifying the reference database consistency, be chatty.\n>   \n> +The following options are specific to 'git refs list':\n> +\n> +include::for-each-ref-options.adoc[]\n> +\n>   KNOWN LIMITATIONS\n>   -----------------\n>   \n> diff --git a/builtin/refs.c b/builtin/refs.c\n> index 998d2a2c1c..848a7c9072 100644\n> --- a/builtin/refs.c\n> +++ b/builtin/refs.c\n> @@ -6,6 +6,7 @@\n>   #include \"refs.h\"\n>   #include \"strbuf.h\"\n>   #include \"worktree.h\"\n> +#include \"for-each-ref.h\"\n>   \n>   #define REFS_MIGRATE_USAGE \\\n>   \tN_(\"git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\")\n> @@ -101,6 +102,17 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,\n>   \treturn ret;\n>   }\n>   \n> +static int cmd_refs_list(int argc, const char **argv, const char *prefix,\n> +\t\t\t   struct repository *repo)\n> +{\n> +\tstatic char const * const refs_list_usage[] = {\n> +\t\tN_(\"git refs list \" COMMON_USAGE_FOR_EACH_REF),\n> +\t\tNULL\n> +\t};\n> +\n> +\treturn for_each_ref_core(argc, argv, prefix, repo, refs_list_usage);\n> +}\n> +\n>   int cmd_refs(int argc,\n>   \t     const char **argv,\n>   \t     const char *prefix,\n> @@ -109,12 +121,14 @@ int cmd_refs(int argc,\n>   \tconst char * const refs_usage[] = {\n>   \t\tREFS_MIGRATE_USAGE,\n>   \t\tREFS_VERIFY_USAGE,\n> +\t\t\"git refs list \" COMMON_USAGE_FOR_EACH_REF,\n>   \t\tNULL,\n>   \t};\n>   \tparse_opt_subcommand_fn *fn = NULL;\n>   \tstruct option opts[] = {\n>   \t\tOPT_SUBCOMMAND(\"migrate\", &fn, cmd_refs_migrate),\n>   \t\tOPT_SUBCOMMAND(\"verify\", &fn, cmd_refs_verify),\n> +\t\tOPT_SUBCOMMAND(\"list\", &fn, cmd_refs_list),\n>   \t\tOPT_END(),\n>   \t};\n>   \n\n"},{"id":"523206","messageId":"xmqqseibm7ap.fsf@gitster.g","threadId":"63703","inReplyTo":"2d2f823d-6e85-44a0-85d2-d45d4dc287fc@gmail.com","subject":"Re: [GSoC][RFC PATCH v4 3/5] builtin/refs: add list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-01T14:43:26Z","receivedAt":"2025-08-01T14:43:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> I find the behavior of for-each-ref quite confusing so I wonder if we\n> really want to copy it to the new command. For example\n>\n>     git for-each-ref 'refs/h*'\n>\n> does not print anything but\n>\n>     git for-each-ref 'refs/heads/m*'\n>\n> prints all the branches beginning with m. Another example is\n>\n>     git for-each-ref 'refs/heads'\n>\n> prints all the branches but\n>\n>     git for-each-ref 'refs/heads*'\n>\n> prints nothing. To me it would be much easier to understand if the\n> pattern always required an explicit wildcard (or possible always did a\n> prefix match in the absence of a wildcard).\n\nThanks for raising this point.\n\nOne point you are wrong is 'refs/heads/m*'; it does not list all the\nbranches that begin with \"m\".  It will not show \"refs/heads/mid/night\"\neven though it may show \"refs/heads/morning'.\n\nI do not think we want to require wildcard (in other words,\nrefs/heads/ and refs/heads that result in the prefix matching\nbehaviour that is anchored at hierarchy boundary is a good thing,\nand we should not require refs/heads/* to get it).\n\nAs the \"list the refs with various criteria\" feature itself is\nshared with \"refs list\", it would make the entire system even more\nconfusing if their criteria to choose which ones to show are\ndifferent.\n\nI _think_ the current selection criteria is basically the prefix\nmatch that is anchored at hierarchy boundary, and a single asterisk\nexpands only to a single hierarchy element without crossing\nhierarchy boundary.  It is very handy that refs/heads/* expands to\nthe main integration branches (master, next, seen, maint-*) without\nshowing pw/3.0-commentchar-auto-deprecation branch and others.\n\nWhat does a double-asterisk currently do in these patterns?  If it\nis not doing anything useful, perhaps we should make it match any\nletter, without getting constrained by hierarchy boundaries?  IOW,\na \"fix\" might be to make sure the following happens?\n\n - \"refs/heads/m*\" matches all local branches whose name starts with\n   'm' like 'morning', but not the ones inside subhierarchies that\n   start with 'm' like 'mid/night'.\n\n - \"refs/heads/m**\" matches all local branches whose name starts\n   with 'm' and in the ones inside subhierarchies that start with\n   'm'.\n\nThanks.\n\n"},{"id":"523216","messageId":"c4d9b25a-2a62-41bf-90e6-455c62b03aa3@gmail.com","threadId":"63703","inReplyTo":"xmqqseibm7ap.fsf@gitster.g","subject":"Re: [GSoC][RFC PATCH v4 3/5] builtin/refs: add list subcommand","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-08-01T15:49:40Z","receivedAt":"2025-08-01T15:49:45Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 01/08/2025 15:43, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n> \n>> I find the behavior of for-each-ref quite confusing so I wonder if we\n>> really want to copy it to the new command. For example\n>>\n>>      git for-each-ref 'refs/h*'\n>>\n>> does not print anything but\n>>\n>>      git for-each-ref 'refs/heads/m*'\n>>\n>> prints all the branches beginning with m. Another example is\n>>\n>>      git for-each-ref 'refs/heads'\n>>\n>> prints all the branches but\n>>\n>>      git for-each-ref 'refs/heads*'\n>>\n>> prints nothing. To me it would be much easier to understand if the\n>> pattern always required an explicit wildcard (or possible always did a\n>> prefix match in the absence of a wildcard).\n> \n> Thanks for raising this point.\n> \n> One point you are wrong is 'refs/heads/m*'; it does not list all the\n> branches that begin with \"m\".  It will not show \"refs/heads/mid/night\"\n> even though it may show \"refs/heads/morning'.\n\nThanks for correcting that\n\n> I do not think we want to require wildcard (in other words,\n> refs/heads/ and refs/heads that result in the prefix matching\n> behaviour that is anchored at hierarchy boundary is a good thing,\n> and we should not require refs/heads/* to get it).\n\nIt is confusing that refs/heads does a prefix match but refs/heads/m \ndoes not (unless there is a hierarchy boundary after the m). Maybe we \nshould require a trailing slash to trigger a prefix match?\n> As the \"list the refs with various criteria\" feature itself is\n> shared with \"refs list\", it would make the entire system even more\n> confusing if their criteria to choose which ones to show are\n> different.\n> \n> I _think_ the current selection criteria is basically the prefix\n> match that is anchored at hierarchy boundary, and a single asterisk\n> expands only to a single hierarchy element without crossing\n> hierarchy boundary.\n\nHaving had a bit of a play with different patterns I think that's right.\n> It is very handy that refs/heads/* expands to\n> the main integration branches (master, next, seen, maint-*) without\n> showing pw/3.0-commentchar-auto-deprecation branch and others.\n> \n> What does a double-asterisk currently do in these patterns?\n\nrefs/heads/m** seems to behave like refs/heads/m*. I'm a bit surprised \nby that as for-each-ref seems to set WM_PATHNAME and I thought that our \nwildmatch code used '**' to match any character in that case.\n\n> If it\n> is not doing anything useful, perhaps we should make it match any\n> letter, without getting constrained by hierarchy boundaries?  IOW,\n> a \"fix\" might be to make sure the following happens?\n> \n>   - \"refs/heads/m*\" matches all local branches whose name starts with\n>     'm' like 'morning', but not the ones inside subhierarchies that\n>     start with 'm' like 'mid/night'.\n> \n>   - \"refs/heads/m**\" matches all local branches whose name starts\n>     with 'm' and in the ones inside subhierarchies that start with\n>     'm'.\n\nThat sounds like a good idea\n\nThanks\n\nPhillip\n\n"},{"id":"523230","messageId":"xmqqwm7nklqf.fsf@gitster.g","threadId":"63703","inReplyTo":"c4d9b25a-2a62-41bf-90e6-455c62b03aa3@gmail.com","subject":"Re: [GSoC][RFC PATCH v4 3/5] builtin/refs: add list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-01T17:14:32Z","receivedAt":"2025-08-01T17:14:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> It is confusing that refs/heads does a prefix match but refs/heads/m\n> does not (unless there is a hierarchy boundary after the m).\n\nAnd refs/hea would not show branches, for the same reason.  It is\nwhat \"anchored at hierarchy boundary\" in the message you are\nresponding to means.  In other words, it is not a simple textual\nprefix match.\n"},{"id":"523374","messageId":"CAPhwyn28L87xu6iPmSC=z6LdvSaN94J0wM9mH6jaC0kGncv_kA@mail.gmail.com","threadId":"63703","inReplyTo":"c4d9b25a-2a62-41bf-90e6-455c62b03aa3@gmail.com","subject":"Re: [GSoC][RFC PATCH v4 3/5] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-04T06:32:25Z","receivedAt":"2025-08-04T06:32:38Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"On Fri, 1 Aug 2025 at 21:19, Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 01/08/2025 15:43, Junio C Hamano wrote:\n> > Phillip Wood <phillip.wood123@gmail.com> writes:\n> >\n[snip]\n> > If it\n> > is not doing anything useful, perhaps we should make it match any\n> > letter, without getting constrained by hierarchy boundaries?  IOW,\n> > a \"fix\" might be to make sure the following happens?\n> >\n> >   - \"refs/heads/m*\" matches all local branches whose name starts with\n> >     'm' like 'morning', but not the ones inside subhierarchies that\n> >     start with 'm' like 'mid/night'.\n> >\n> >   - \"refs/heads/m**\" matches all local branches whose name starts\n> >     with 'm' and in the ones inside subhierarchies that start with\n> >     'm'.\n>\n> That sounds like a good idea\n>\n> Thanks\n>\n> Phillip\n>\nHi Junio and Phillip,\n\nThanks for the valuable discussion.\n\nI think it makes the most sense to implement the ** wildcard enhancement in a\nseparate patch series. The goals are logically different, and separating them\nwill be better for the commit history.\n\nI'll start working on that series and will send it for review once this one is\nwrapped up.\n\nThanks.\n"},{"id":"523378","messageId":"CAPhwyn0X1hS89B6p_NQKunrdO1EHMkwtF79RvA1riM6EH4PRJw@mail.gmail.com","threadId":"63703","inReplyTo":"aIxWlX36V6DcxWa0@pks.im","subject":"Re: [GSoC][RFC PATCH v4 2/5] builtin/for-each-ref: factor out core logic into a helper","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-04T06:34:14Z","receivedAt":"2025-08-04T06:34:26Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"On Fri, 1 Aug 2025 at 11:24, Patrick Steinhardt <ps@pks.im> wrote:\n>\n> On Thu, Jul 31, 2025 at 02:30:37PM +0530, Meet Soni wrote:\n> > diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\n> > index 3d2207ec77..bbc0e5ad1c 100644\n> > --- a/builtin/for-each-ref.c\n> > +++ b/builtin/for-each-ref.c\n> > @@ -7,19 +7,9 @@\n> >  #include \"ref-filter.h\"\n> >  #include \"strbuf.h\"\n> >  #include \"strvec.h\"\n> > +#include \"for-each-ref.h\"\n>\n> Let's keep the includes alphabetically sorted.\n>\n> > diff --git a/t/t0450/adoc-help-mismatches b/t/t0450/adoc-help-mismatches\n> > index 06b469bdee..2c6ecd5fc8 100644\n> > --- a/t/t0450/adoc-help-mismatches\n> > +++ b/t/t0450/adoc-help-mismatches\n> > @@ -17,7 +17,6 @@ fast-export\n> >  fast-import\n> >  fetch-pack\n> >  fmt-merge-msg\n> > -for-each-ref\n> >  format-patch\n> >  fsck-objects\n> >  fsmonitor--daemon\n>\n> Everything else looks sensible, but this change is surprising as it\n> wasn't mentioned in the commit message. I see that you changed the usage\n> though to match what we have in the man page. That's a good change\n> overall, but should probably be moved into a separate commit so that\n> this commit here is not changing any behaviour.\n>\n> Patrick\n\nMakes sense, I'll send the updated version.\n\nThanks.\n"},{"id":"523402","messageId":"20250804092255.1092973-1-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250731090040.1625303-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v5 0/6] Add refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-04T09:22:49Z","receivedAt":"2025-08-04T09:23:03Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Hello Everyone,\n\nThis is the fifth version of the patch series that introduces the git\nrefs list subcommand.\n\nchanges in v5:\n  - moved changes that fixed the test into a separate commit.\n\n---\n(v1 cover-letter text)\n\nThis patch series introduces `git refs list` as a modern replacement for\n`git for-each-ref`, as part of an effort to consolidate ref-related\nfunctionality under a unified `git refs` command.\n\nGit's ref-related operations are currently handled by several distinct\ncommands, such as `git show-ref`, `git for-each-ref`, `git update-ref`,\n`git pack-refs`, etc. This distribution has a few practical drawbacks:\n\n- Users need to rely on multiple commands for related tasks involving\n  refs.\n\n- The commands may differ slightly in behavior and option syntax,\n  leading to inconsistency.\n\nWe propose a long-term consolidation effort to bring ref-related\nsubcommands under the umbrella of a single command: `git refs`.\n\nThe implementation of `git refs list` is functionally identical to `git\nfor-each-ref`. It reuses the same internal logic (cmd_for_each_ref) to\nensure complete backward compatibility. The purpose of this patch is not\nto introduce new behavior but to provide an alternate entry point under\nthe consolidated `git refs` namespace.\n\nThe motivation behind this change is twofold:\n\n- Consolidation: Centralizing ref-related operations makes them easier\n  to discover, use, and maintain.\n\n- Evolution: While the initial goal is parity with existing commands,\n  this consolidation allows for careful reconsideration of which\n  features are essential. Over time, we can:\n\n  - Remove legacy or obscure options that are no longer needed.\n  - Add improvements that wouldn't make sense to bolt onto legacy\n    commands.\n  - Offering a more consistent and user-friendly surface.\n\nTo verify backward compatibility, this patch also includes a test\n`t/t1461-refs-list.sh`, which runs the full `t6300-for-each-ref.sh` test\nusing `git refs list`. The test uses ${GIT_REFS_LIST_CMD:-for-each-ref}\nto allow substitution without duplicating tests.\n\nThis patch is deliberately conservative: it introduces no behavioral\nchanges and leaves `for-each-ref` untouched. The goal is to lay\ngroundwork and demonstrate viability of ref consolidation within `git\nrefs`.\n\nGoing forward, I'd like to initiate a discussion on what the ideal\nsurface of `git refs list` should look like. Which options and features\nfrom `for-each-ref` should be carried over? Are there any that are\nobsolete or overly niche? What improvements might be worth considering\nnow that we have a new, consolidated interface?\n\nFeedback on this, especially from those who rely on `for-each-ref` in\nscripts or tooling would be very helpful.\n\nMeet Soni (6):\n  doc: factor out common option\n  builtin/for-each-ref: align usage string with the man page\n  builtin/for-each-ref: factor out core logic into a helper\n  builtin/refs: add list subcommand\n  t6300: refactor tests to be shareable\n  t: add test for git refs list subcommand\n\n Documentation/for-each-ref-options.adoc |   79 +\n Documentation/git-for-each-ref.adoc     |   80 +-\n Documentation/git-refs.adoc             |   16 +\n builtin/for-each-ref.c                  |   35 +-\n builtin/refs.c                          |   14 +\n for-each-ref.h                          |   26 +\n t/for-each-ref-tests.sh                 | 2141 +++++++++++++++++++++++\n t/meson.build                           |    1 +\n t/t0450/adoc-help-mismatches            |    1 -\n t/t1461-refs-list.sh                    |    8 +\n t/t6300-for-each-ref.sh                 | 2140 +---------------------\n 11 files changed, 2307 insertions(+), 2234 deletions(-)\n create mode 100644 Documentation/for-each-ref-options.adoc\n create mode 100644 for-each-ref.h\n create mode 100644 t/for-each-ref-tests.sh\n create mode 100755 t/t1461-refs-list.sh\n\nRange-diff against v4:\n1:  d2fa47a2b9 = 1:  d2fa47a2b9 doc: factor out common option\n-:  ---------- > 2:  48a006dca9 builtin/for-each-ref: align usage string with the man page\n2:  5084db4d14 ! 3:  b7788d477a builtin/for-each-ref: factor out core logic into a helper\n    @@ Commit message\n     \n      ## builtin/for-each-ref.c ##\n     @@\n    - #include \"ref-filter.h\"\n    + #include \"builtin.h\"\n    + #include \"commit.h\"\n    + #include \"config.h\"\n    ++#include \"for-each-ref.h\"\n    + #include \"gettext.h\"\n    + #include \"object.h\"\n    + #include \"parse-options.h\"\n    +@@\n      #include \"strbuf.h\"\n      #include \"strvec.h\"\n    -+#include \"for-each-ref.h\"\n      \n    +-#define COMMON_USAGE_FOR_EACH_REF \\\n    +-\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n    +-\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n    +-\t\"                         [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n    +-\t\"                         [--points-at=<object>]\\n\" \\\n    +-\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n    +-\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n    +-\t\"                         [--exclude=<pattern> ...]\"\n    +-\n     -static char const * const for_each_ref_usage[] = {\n    --\tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n    --\tN_(\"git for-each-ref [--points-at <object>]\"),\n    --\tN_(\"git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]\"),\n    --\tN_(\"git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]\"),\n    +-\t\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF,\n     -\tNULL\n     -};\n     -\n    @@ for-each-ref.h (new)\n     +#ifndef FOR_EACH_REF_H\n     +#define FOR_EACH_REF_H\n     +\n    ++struct repository;\n    ++\n     +/*\n     + * Shared usage string for options common to git-for-each-ref(1)\n     + * and git-refs-list(1). The command-specific part (e.g., \"git refs list \")\n    @@ for-each-ref.h (new)\n     +\t\t      struct repository *repo, const char *const *usage);\n     +\n     +#endif /* FOR_EACH_REF_H */\n    -\n    - ## t/t0450/adoc-help-mismatches ##\n    -@@ t/t0450/adoc-help-mismatches: fast-export\n    - fast-import\n    - fetch-pack\n    - fmt-merge-msg\n    --for-each-ref\n    - format-patch\n    - fsck-objects\n    - fsmonitor--daemon\n3:  1a15cd454b = 4:  97088dab96 builtin/refs: add list subcommand\n4:  29c41d682b = 5:  abe9df9c4f t6300: refactor tests to be shareable\n5:  0fd1f714c9 = 6:  a037a47dcd t: add test for git refs list subcommand\n-- \n2.34.1\n\n"},{"id":"523403","messageId":"20250804092255.1092973-2-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250804092255.1092973-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v5 1/6] doc: factor out common option","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-04T09:22:50Z","receivedAt":"2025-08-04T09:23:08Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"In preparation for adding documentation for `git refs list`, factor out\nthe common options from the `git-for-each-ref` man page into a\nshareable file `for-each-ref-options.adoc` and update\n`git-for-each-ref.adoc` to use an `include::` macro.\n\nThis change is a pure refactoring and results in no change to the\nfinal rendered documentation for `for-each-ref`.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/for-each-ref-options.adoc | 79 ++++++++++++++++++++++++\n Documentation/git-for-each-ref.adoc     | 80 +------------------------\n 2 files changed, 80 insertions(+), 79 deletions(-)\n create mode 100644 Documentation/for-each-ref-options.adoc\n\ndiff --git a/Documentation/for-each-ref-options.adoc b/Documentation/for-each-ref-options.adoc\nnew file mode 100644\nindex 0000000000..5f3a85bf64\n--- /dev/null\n+++ b/Documentation/for-each-ref-options.adoc\n@@ -0,0 +1,79 @@\n+<pattern>...::\n+\tIf one or more patterns are given, only refs are shown that\n+\tmatch against at least one pattern, either using fnmatch(3) or\n+\tliterally, in the latter case matching completely or from the\n+\tbeginning up to a slash.\n+\n+--stdin::\n+\tIf `--stdin` is supplied, then the list of patterns is read from\n+\tstandard input instead of from the argument list.\n+\n+--count=<count>::\n+\tBy default the command shows all refs that match\n+\t`<pattern>`.  This option makes it stop after showing\n+\tthat many refs.\n+\n+--sort=<key>::\n+\tA field name to sort on.  Prefix `-` to sort in\n+\tdescending order of the value.  When unspecified,\n+\t`refname` is used.  You may use the --sort=<key> option\n+\tmultiple times, in which case the last key becomes the primary\n+\tkey.\n+\n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from a ref being shown and\n+\tthe object it points at. In addition, the string literal `%%`\n+\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n+\tthe character with hex code `xx`. For example, `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n++\n+When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n+TAB %(refname)`.\n+\n+--color[=<when>]::\n+\tRespect any colors specified in the `--format` option. The\n+\t`<when>` field must be one of `always`, `never`, or `auto` (if\n+\t`<when>` is absent, behave as if `always` was given).\n+\n+--shell::\n+--perl::\n+--python::\n+--tcl::\n+\tIf given, strings that substitute `%(fieldname)`\n+\tplaceholders are quoted as string literals suitable for\n+\tthe specified host language.  This is meant to produce\n+\ta scriptlet that can directly be `eval`ed.\n+\n+--points-at=<object>::\n+\tOnly list refs which points at the given object.\n+\n+--merged[=<object>]::\n+\tOnly list refs whose tips are reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--no-merged[=<object>]::\n+\tOnly list refs whose tips are not reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--contains[=<object>]::\n+\tOnly list refs which contain the specified commit (HEAD if not\n+\tspecified).\n+\n+--no-contains[=<object>]::\n+\tOnly list refs which don't contain the specified commit (HEAD\n+\tif not specified).\n+\n+--ignore-case::\n+\tSorting and filtering refs are case insensitive.\n+\n+--omit-empty::\n+\tDo not print a newline after formatted refs where the format expands\n+\tto the empty string.\n+\n+--exclude=<pattern>::\n+\tIf one or more patterns are given, only refs which do not match\n+\tany excluded pattern(s) are shown. Matching is done using the\n+\tsame rules as `<pattern>` above.\n+\n+--include-root-refs::\n+\tList root refs (HEAD and pseudorefs) apart from regular refs.\ndiff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\nindex 5ef89fc0fe..c3cf1752e3 100644\n--- a/Documentation/git-for-each-ref.adoc\n+++ b/Documentation/git-for-each-ref.adoc\n@@ -28,85 +28,7 @@ host language allowing their direct evaluation in that language.\n \n OPTIONS\n -------\n-<pattern>...::\n-\tIf one or more patterns are given, only refs are shown that\n-\tmatch against at least one pattern, either using fnmatch(3) or\n-\tliterally, in the latter case matching completely or from the\n-\tbeginning up to a slash.\n-\n---stdin::\n-\tIf `--stdin` is supplied, then the list of patterns is read from\n-\tstandard input instead of from the argument list.\n-\n---count=<count>::\n-\tBy default the command shows all refs that match\n-\t`<pattern>`.  This option makes it stop after showing\n-\tthat many refs.\n-\n---sort=<key>::\n-\tA field name to sort on.  Prefix `-` to sort in\n-\tdescending order of the value.  When unspecified,\n-\t`refname` is used.  You may use the --sort=<key> option\n-\tmultiple times, in which case the last key becomes the primary\n-\tkey.\n-\n---format=<format>::\n-\tA string that interpolates `%(fieldname)` from a ref being shown and\n-\tthe object it points at. In addition, the string literal `%%`\n-\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n-\tthe character with hex code `xx`. For example, `%00` interpolates to\n-\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n-+\n-When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n-TAB %(refname)`.\n-\n---color[=<when>]::\n-\tRespect any colors specified in the `--format` option. The\n-\t`<when>` field must be one of `always`, `never`, or `auto` (if\n-\t`<when>` is absent, behave as if `always` was given).\n-\n---shell::\n---perl::\n---python::\n---tcl::\n-\tIf given, strings that substitute `%(fieldname)`\n-\tplaceholders are quoted as string literals suitable for\n-\tthe specified host language.  This is meant to produce\n-\ta scriptlet that can directly be `eval`ed.\n-\n---points-at=<object>::\n-\tOnly list refs which points at the given object.\n-\n---merged[=<object>]::\n-\tOnly list refs whose tips are reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---no-merged[=<object>]::\n-\tOnly list refs whose tips are not reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---contains[=<object>]::\n-\tOnly list refs which contain the specified commit (HEAD if not\n-\tspecified).\n-\n---no-contains[=<object>]::\n-\tOnly list refs which don't contain the specified commit (HEAD\n-\tif not specified).\n-\n---ignore-case::\n-\tSorting and filtering refs are case insensitive.\n-\n---omit-empty::\n-\tDo not print a newline after formatted refs where the format expands\n-\tto the empty string.\n-\n---exclude=<pattern>::\n-\tIf one or more patterns are given, only refs which do not match\n-\tany excluded pattern(s) are shown. Matching is done using the\n-\tsame rules as `<pattern>` above.\n-\n---include-root-refs::\n-\tList root refs (HEAD and pseudorefs) apart from regular refs.\n+include::for-each-ref-options.adoc[]\n \n FIELD NAMES\n -----------\n-- \n2.34.1\n\n"},{"id":"523404","messageId":"20250804092255.1092973-3-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250804092255.1092973-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v5 2/6] builtin/for-each-ref: align usage string with the man page","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-04T09:22:51Z","receivedAt":"2025-08-04T09:23:13Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Usage string for `git for-each-ref` was out of sync with its official\ndocumentation. The test `t0450-txt-doc-vs-help.sh` was marked as broken\ndue to this.\n\nUpdate the usage string to match the documentation. This allows the test\nto pass, so remove the corresponding 'known breakage' marker from the\ntest file.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n builtin/for-each-ref.c       | 14 ++++++++++----\n t/t0450/adoc-help-mismatches |  1 -\n 2 files changed, 10 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 3d2207ec77..b2186f9f9a 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -8,11 +8,17 @@\n #include \"strbuf.h\"\n #include \"strvec.h\"\n \n+#define COMMON_USAGE_FOR_EACH_REF \\\n+\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t\"                         [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n+\t\"                         [--points-at=<object>]\\n\" \\\n+\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t\"                         [--exclude=<pattern> ...]\"\n+\n static char const * const for_each_ref_usage[] = {\n-\tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n-\tN_(\"git for-each-ref [--points-at <object>]\"),\n-\tN_(\"git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]\"),\n-\tN_(\"git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]\"),\n+\t\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF,\n \tNULL\n };\n \ndiff --git a/t/t0450/adoc-help-mismatches b/t/t0450/adoc-help-mismatches\nindex 06b469bdee..2c6ecd5fc8 100644\n--- a/t/t0450/adoc-help-mismatches\n+++ b/t/t0450/adoc-help-mismatches\n@@ -17,7 +17,6 @@ fast-export\n fast-import\n fetch-pack\n fmt-merge-msg\n-for-each-ref\n format-patch\n fsck-objects\n fsmonitor--daemon\n-- \n2.34.1\n\n"},{"id":"523405","messageId":"20250804092255.1092973-4-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250804092255.1092973-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v5 3/6] builtin/for-each-ref: factor out core logic into a helper","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-04T09:22:52Z","receivedAt":"2025-08-04T09:23:17Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"The implementation of `git for-each-ref` is monolithic within\n`cmd_for_each_ref()`, making it impossible to share its logic with other\ncommands. To enable code reuse for the upcoming `git refs list`\nsubcommand, refactor the core logic into a shared helper function.\n\nIntroduce a new `for-each-ref.h` header to define the public interface\nfor this shared logic. It contains the declaration for a new helper\nfunction, `for_each_ref_core()`, and a macro for the common usage\noptions.\n\nMove the option parsing, filtering, and formatting logic from\n`cmd_for_each_ref()` into a new helper function named\n`for_each_ref_core()`. This helper is made generic by accepting the\ncommand's usage string as a parameter.\n\nThe original `cmd_for_each_ref()` is simplified to a thin wrapper that\nis only responsible for defining its specific usage array and calling\nthe shared helper.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n builtin/for-each-ref.c | 41 +++++++++++++++++++----------------------\n for-each-ref.h         | 26 ++++++++++++++++++++++++++\n 2 files changed, 45 insertions(+), 22 deletions(-)\n create mode 100644 for-each-ref.h\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex b2186f9f9a..2d4945379a 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -1,6 +1,7 @@\n #include \"builtin.h\"\n #include \"commit.h\"\n #include \"config.h\"\n+#include \"for-each-ref.h\"\n #include \"gettext.h\"\n #include \"object.h\"\n #include \"parse-options.h\"\n@@ -8,24 +9,7 @@\n #include \"strbuf.h\"\n #include \"strvec.h\"\n \n-#define COMMON_USAGE_FOR_EACH_REF \\\n-\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n-\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n-\t\"                         [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n-\t\"                         [--points-at=<object>]\\n\" \\\n-\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n-\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n-\t\"                         [--exclude=<pattern> ...]\"\n-\n-static char const * const for_each_ref_usage[] = {\n-\t\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF,\n-\tNULL\n-};\n-\n-int cmd_for_each_ref(int argc,\n-\t\t     const char **argv,\n-\t\t     const char *prefix,\n-\t\t     struct repository *repo)\n+int for_each_ref_core(int argc, const char **argv, const char *prefix, struct repository *repo, const char *const *usage)\n {\n \tstruct ref_sorting *sorting;\n \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n@@ -73,17 +57,17 @@ int cmd_for_each_ref(int argc,\n \t/* Set default (refname) sorting */\n \tstring_list_append(&sorting_options, \"refname\");\n \n-\tparse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);\n+\tparse_options(argc, argv, prefix, opts, usage, 0);\n \tif (format.array_opts.max_count < 0) {\n \t\terror(\"invalid --count argument: `%d'\", format.array_opts.max_count);\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(usage, opts);\n \t}\n \tif (HAS_MULTI_BITS(format.quote_style)) {\n \t\terror(\"more than one quoting style?\");\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(usage, opts);\n \t}\n \tif (verify_ref_format(&format))\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(usage, opts);\n \n \tsorting = ref_sorting_options(&sorting_options);\n \tref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);\n@@ -117,3 +101,16 @@ int cmd_for_each_ref(int argc,\n \tstrvec_clear(&vec);\n \treturn 0;\n }\n+\n+int cmd_for_each_ref(int argc,\n+\t\t     const char **argv,\n+\t\t     const char *prefix,\n+\t\t     struct repository *repo)\n+{\n+\tstatic char const * const for_each_ref_usage[] = {\n+\t\tN_(\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF),\n+\t\tNULL\n+\t};\n+\n+\treturn for_each_ref_core(argc, argv, prefix, repo, for_each_ref_usage);\n+}\ndiff --git a/for-each-ref.h b/for-each-ref.h\nnew file mode 100644\nindex 0000000000..1662d25417\n--- /dev/null\n+++ b/for-each-ref.h\n@@ -0,0 +1,26 @@\n+#ifndef FOR_EACH_REF_H\n+#define FOR_EACH_REF_H\n+\n+struct repository;\n+\n+/*\n+ * Shared usage string for options common to git-for-each-ref(1)\n+ * and git-refs-list(1). The command-specific part (e.g., \"git refs list \")\n+ * must be prepended by the caller.\n+ */\n+#define COMMON_USAGE_FOR_EACH_REF \\\n+\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t\"                         [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n+\t\"                         [--points-at=<object>]\\n\" \\\n+\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t\"                         [--exclude=<pattern> ...]\"\n+\n+/*\n+ * The core logic for for-each-ref and its clones.\n+ */\n+int for_each_ref_core(int argc, const char **argv, const char *prefix,\n+\t\t      struct repository *repo, const char *const *usage);\n+\n+#endif /* FOR_EACH_REF_H */\n-- \n2.34.1\n\n"},{"id":"523406","messageId":"20250804092255.1092973-5-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250804092255.1092973-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v5 4/6] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-04T09:22:53Z","receivedAt":"2025-08-04T09:23:21Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Git's reference management is distributed across multiple commands. As\npart of an ongoing effort to consolidate and modernize reference\nhandling, introduce a `list` subcommand under the `git refs` umbrella as\na replacement for `git for-each-ref`.\n\nImplement `cmd_refs_list` by having it call the `for_each_ref_core()`\nhelper function. This helper was factored out of the original\n`cmd_for_each_ref` in a preceding commit, allowing both commands to\nshare the same core logic as independent peers.\n\nAdd documentation for the new command. The man page leverages the shared\noptions file, created in a previous commit, by using the AsciiDoc\n`include::` macro to ensure consistency with git-for-each-ref(1).\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/git-refs.adoc | 16 ++++++++++++++++\n builtin/refs.c              | 14 ++++++++++++++\n 2 files changed, 30 insertions(+)\n\ndiff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\nindex 4d6dc994f9..ee563aa7e0 100644\n--- a/Documentation/git-refs.adoc\n+++ b/Documentation/git-refs.adoc\n@@ -11,6 +11,13 @@ SYNOPSIS\n [synopsis]\n git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n git refs verify [--strict] [--verbose]\n+git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n+\t      [(--sort=<key>)...] [--format=<format>]\n+\t      [--include-root-refs] [ --stdin | <pattern>... ]\n+\t      [--points-at=<object>]\n+\t      [--merged[=<object>]] [--no-merged[=<object>]]\n+\t      [--contains[=<object>]] [--no-contains[=<object>]]\n+\t      [--exclude=<pattern> ...]\n \n DESCRIPTION\n -----------\n@@ -26,6 +33,11 @@ migrate::\n verify::\n \tVerify reference database consistency.\n \n+list::\n+\tList references in the repository with support for filtering,\n+\tformatting, and sorting. This subcommand is an alias for\n+\tlinkgit:git-for-each-ref[1] and offers identical functionality.\n+\n OPTIONS\n -------\n \n@@ -57,6 +69,10 @@ The following options are specific to 'git refs verify':\n --verbose::\n \tWhen verifying the reference database consistency, be chatty.\n \n+The following options are specific to 'git refs list':\n+\n+include::for-each-ref-options.adoc[]\n+\n KNOWN LIMITATIONS\n -----------------\n \ndiff --git a/builtin/refs.c b/builtin/refs.c\nindex 998d2a2c1c..848a7c9072 100644\n--- a/builtin/refs.c\n+++ b/builtin/refs.c\n@@ -6,6 +6,7 @@\n #include \"refs.h\"\n #include \"strbuf.h\"\n #include \"worktree.h\"\n+#include \"for-each-ref.h\"\n \n #define REFS_MIGRATE_USAGE \\\n \tN_(\"git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\")\n@@ -101,6 +102,17 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,\n \treturn ret;\n }\n \n+static int cmd_refs_list(int argc, const char **argv, const char *prefix,\n+\t\t\t   struct repository *repo)\n+{\n+\tstatic char const * const refs_list_usage[] = {\n+\t\tN_(\"git refs list \" COMMON_USAGE_FOR_EACH_REF),\n+\t\tNULL\n+\t};\n+\n+\treturn for_each_ref_core(argc, argv, prefix, repo, refs_list_usage);\n+}\n+\n int cmd_refs(int argc,\n \t     const char **argv,\n \t     const char *prefix,\n@@ -109,12 +121,14 @@ int cmd_refs(int argc,\n \tconst char * const refs_usage[] = {\n \t\tREFS_MIGRATE_USAGE,\n \t\tREFS_VERIFY_USAGE,\n+\t\t\"git refs list \" COMMON_USAGE_FOR_EACH_REF,\n \t\tNULL,\n \t};\n \tparse_opt_subcommand_fn *fn = NULL;\n \tstruct option opts[] = {\n \t\tOPT_SUBCOMMAND(\"migrate\", &fn, cmd_refs_migrate),\n \t\tOPT_SUBCOMMAND(\"verify\", &fn, cmd_refs_verify),\n+\t\tOPT_SUBCOMMAND(\"list\", &fn, cmd_refs_list),\n \t\tOPT_END(),\n \t};\n \n-- \n2.34.1\n\n"},{"id":"523408","messageId":"20250804092255.1092973-6-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250804092255.1092973-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v5 5/6] t6300: refactor tests to be shareable","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-04T09:22:54Z","receivedAt":"2025-08-04T09:23:29Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"In preparation for adding tests for the new `git refs list` command,\nrefactor the existing t6300 test suite to make its logic shareable.\n\nMove the core test logic from `t6300-for-each-ref.sh` into a new\n`for-each-ref-tests.sh` file. Inside this new script, replace hardcoded\ncalls to \"git for-each-ref\" with the `$git_for_each_ref` variable.\n\nThe original `t6300-for-each-ref.sh` script now becomes a simple\n\"driver\". It is responsible for setting the default value of the\nvariable and then sourcing the test library.\n\nThis new structure follows the established pattern used for sharing\ntests between `git-blame` and `git-annotate` and prepares the test suite\nfor the `refs list` tests to be added in a subsequent commit.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/for-each-ref-tests.sh | 2141 +++++++++++++++++++++++++++++++++++++++\n t/t6300-for-each-ref.sh | 2140 +-------------------------------------\n 2 files changed, 2143 insertions(+), 2138 deletions(-)\n create mode 100644 t/for-each-ref-tests.sh\n\ndiff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh\nnew file mode 100644\nindex 0000000000..e3ad19298a\n--- /dev/null\n+++ b/t/for-each-ref-tests.sh\n@@ -0,0 +1,2141 @@\n+git_for_each_ref=${git_for_each_ref:-git for-each-ref}\n+GNUPGHOME_NOT_USED=$GNUPGHOME\n+. \"$TEST_DIRECTORY\"/lib-gpg.sh\n+. \"$TEST_DIRECTORY\"/lib-terminal.sh\n+\n+# Mon Jul 3 23:18:43 2006 +0000\n+datestamp=1151968723\n+setdate_and_increment () {\n+    GIT_COMMITTER_DATE=\"$datestamp +0200\"\n+    datestamp=$(expr \"$datestamp\" + 1)\n+    GIT_AUTHOR_DATE=\"$datestamp +0200\"\n+    datestamp=$(expr \"$datestamp\" + 1)\n+    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n+}\n+\n+test_object_file_size () {\n+\toid=$(git rev-parse \"$1\")\n+\tpath=\".git/objects/$(test_oid_to_path $oid)\"\n+\ttest_file_size \"$path\"\n+}\n+\n+test_expect_success setup '\n+\t# setup .mailmap\n+\tcat >.mailmap <<-EOF &&\n+\tA Thor <athor@example.com> A U Thor <author@example.com>\n+\tC Mitter <cmitter@example.com> C O Mitter <committer@example.com>\n+\tEOF\n+\n+\tsetdate_and_increment &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Initial\" &&\n+\tgit branch -M main &&\n+\tsetdate_and_increment &&\n+\tgit tag -a -m \"Tagging at $datestamp\" testtag &&\n+\tgit update-ref refs/remotes/origin/main main &&\n+\tgit remote add origin nowhere &&\n+\tgit config branch.main.remote origin &&\n+\tgit config branch.main.merge refs/heads/main &&\n+\tgit remote add myfork elsewhere &&\n+\tgit config remote.pushdefault myfork &&\n+\tgit config push.default current\n+'\n+\n+test_atom () {\n+\tcase \"$1\" in\n+\t\thead) ref=refs/heads/main ;;\n+\t\t tag) ref=refs/tags/testtag ;;\n+\t\t sym) ref=refs/heads/sym ;;\n+\t\t   *) ref=$1 ;;\n+\tesac\n+\tformat=$2\n+\ttest_do=test_expect_${4:-success}\n+\n+\tprintf '%s\\n' \"$3\" >expected\n+\t$test_do $PREREQ \"basic atom: $ref $format\" '\n+\t\t${git_for_each_ref} --format=\"%($format)\" \"$ref\" >actual &&\n+\t\tsanitize_pgp <actual >actual.clean &&\n+\t\ttest_cmp expected actual.clean\n+\t'\n+\n+\t# Automatically test \"contents:size\" atom after testing \"contents\"\n+\tif test \"$format\" = \"contents\"\n+\tthen\n+\t\t# for commit leg, $3 is changed there\n+\t\texpect=$(printf '%s' \"$3\" | wc -c)\n+\t\t$test_do $PREREQ \"basic atom: $ref contents:size\" '\n+\t\t\ttype=$(git cat-file -t \"$ref\") &&\n+\t\t\tcase $type in\n+\t\t\ttag)\n+\t\t\t\t# We cannot use $3 as it expects sanitize_pgp to run\n+\t\t\t\tgit cat-file tag $ref >out &&\n+\t\t\t\texpect=$(tail -n +6 out | wc -c) &&\n+\t\t\t\trm -f out ;;\n+\t\t\ttree | blob)\n+\t\t\t\texpect=\"\" ;;\n+\t\t\tcommit)\n+\t\t\t\t: \"use the calculated expect\" ;;\n+\t\t\t*)\n+\t\t\t\tBUG \"unknown object type\" ;;\n+\t\t\tesac &&\n+\t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n+\t\t\techo $expect >expected &&\n+\t\t\t${git_for_each_ref} --format=\"%(contents:size)\" \"$ref\" >actual &&\n+\t\t\ttest_cmp expected actual\n+\t\t'\n+\tfi\n+}\n+\n+hexlen=$(test_oid hexsz)\n+\n+test_atom head refname refs/heads/main\n+test_atom head refname: refs/heads/main\n+test_atom head refname:short main\n+test_atom head refname:lstrip=1 heads/main\n+test_atom head refname:lstrip=2 main\n+test_atom head refname:lstrip=-1 main\n+test_atom head refname:lstrip=-2 heads/main\n+test_atom head refname:rstrip=1 refs/heads\n+test_atom head refname:rstrip=2 refs\n+test_atom head refname:rstrip=-1 refs\n+test_atom head refname:rstrip=-2 refs/heads\n+test_atom head refname:strip=1 heads/main\n+test_atom head refname:strip=2 main\n+test_atom head refname:strip=-1 main\n+test_atom head refname:strip=-2 heads/main\n+test_atom head upstream refs/remotes/origin/main\n+test_atom head upstream:short origin/main\n+test_atom head upstream:lstrip=2 origin/main\n+test_atom head upstream:lstrip=-2 origin/main\n+test_atom head upstream:rstrip=2 refs/remotes\n+test_atom head upstream:rstrip=-2 refs/remotes\n+test_atom head upstream:strip=2 origin/main\n+test_atom head upstream:strip=-2 origin/main\n+test_atom head push refs/remotes/myfork/main\n+test_atom head push:short myfork/main\n+test_atom head push:lstrip=1 remotes/myfork/main\n+test_atom head push:lstrip=-1 main\n+test_atom head push:rstrip=1 refs/remotes/myfork\n+test_atom head push:rstrip=-1 refs\n+test_atom head push:strip=1 remotes/myfork/main\n+test_atom head push:strip=-1 main\n+test_atom head objecttype commit\n+test_atom head objectsize $((131 + hexlen))\n+test_atom head objectsize:disk $(test_object_file_size refs/heads/main)\n+test_atom head deltabase $ZERO_OID\n+test_atom head objectname $(git rev-parse refs/heads/main)\n+test_atom head objectname:short $(git rev-parse --short refs/heads/main)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n+test_atom head tree $(git rev-parse refs/heads/main^{tree})\n+test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})\n+test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})\n+test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})\n+test_atom head parent ''\n+test_atom head parent:short ''\n+test_atom head parent:short=1 ''\n+test_atom head parent:short=10 ''\n+test_atom head numparent 0\n+test_atom head object ''\n+test_atom head type ''\n+test_atom head raw \"$(git cat-file commit refs/heads/main)\n+\"\n+test_atom head '*objectname' ''\n+test_atom head '*objecttype' ''\n+test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n+test_atom head authorname 'A U Thor'\n+test_atom head authorname:mailmap 'A Thor'\n+test_atom head authoremail '<author@example.com>'\n+test_atom head authoremail:trim 'author@example.com'\n+test_atom head authoremail:localpart 'author'\n+test_atom head authoremail:trim,localpart 'author'\n+test_atom head authoremail:mailmap '<athor@example.com>'\n+test_atom head authoremail:mailmap,trim 'athor@example.com'\n+test_atom head authoremail:trim,mailmap 'athor@example.com'\n+test_atom head authoremail:mailmap,localpart 'athor'\n+test_atom head authoremail:localpart,mailmap 'athor'\n+test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'\n+test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n+test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n+test_atom head committername 'C O Mitter'\n+test_atom head committername:mailmap 'C Mitter'\n+test_atom head committeremail '<committer@example.com>'\n+test_atom head committeremail:trim 'committer@example.com'\n+test_atom head committeremail:localpart 'committer'\n+test_atom head committeremail:localpart,trim 'committer'\n+test_atom head committeremail:mailmap '<cmitter@example.com>'\n+test_atom head committeremail:mailmap,trim 'cmitter@example.com'\n+test_atom head committeremail:trim,mailmap 'cmitter@example.com'\n+test_atom head committeremail:mailmap,localpart 'cmitter'\n+test_atom head committeremail:localpart,mailmap 'cmitter'\n+test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'\n+test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n+test_atom head tag ''\n+test_atom head tagger ''\n+test_atom head taggername ''\n+test_atom head taggeremail ''\n+test_atom head taggeremail:trim ''\n+test_atom head taggeremail:localpart ''\n+test_atom head taggerdate ''\n+test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n+test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n+test_atom head subject 'Initial'\n+test_atom head subject:sanitize 'Initial'\n+test_atom head contents:subject 'Initial'\n+test_atom head body ''\n+test_atom head contents:body ''\n+test_atom head contents:signature ''\n+test_atom head contents 'Initial\n+'\n+test_atom head HEAD '*'\n+\n+test_atom tag refname refs/tags/testtag\n+test_atom tag refname:short testtag\n+test_atom tag upstream ''\n+test_atom tag push ''\n+test_atom tag objecttype tag\n+test_atom tag objectsize $((114 + hexlen))\n+test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)\n+test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)\n+test_atom tag deltabase $ZERO_OID\n+test_atom tag '*deltabase' $ZERO_OID\n+test_atom tag objectname $(git rev-parse refs/tags/testtag)\n+test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n+test_atom tag tree ''\n+test_atom tag tree:short ''\n+test_atom tag tree:short=1 ''\n+test_atom tag tree:short=10 ''\n+test_atom tag parent ''\n+test_atom tag parent:short ''\n+test_atom tag parent:short=1 ''\n+test_atom tag parent:short=10 ''\n+test_atom tag numparent ''\n+test_atom tag object $(git rev-parse refs/tags/testtag^0)\n+test_atom tag type 'commit'\n+test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})\n+test_atom tag '*objecttype' 'commit'\n+test_atom tag author ''\n+test_atom tag authorname ''\n+test_atom tag authorname:mailmap ''\n+test_atom tag authoremail ''\n+test_atom tag authoremail:trim ''\n+test_atom tag authoremail:localpart ''\n+test_atom tag authoremail:trim,localpart ''\n+test_atom tag authoremail:mailmap ''\n+test_atom tag authoremail:mailmap,trim ''\n+test_atom tag authoremail:trim,mailmap ''\n+test_atom tag authoremail:mailmap,localpart ''\n+test_atom tag authoremail:localpart,mailmap ''\n+test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''\n+test_atom tag authordate ''\n+test_atom tag committer ''\n+test_atom tag committername ''\n+test_atom tag committername:mailmap ''\n+test_atom tag committeremail ''\n+test_atom tag committeremail:trim ''\n+test_atom tag committeremail:localpart ''\n+test_atom tag committeremail:localpart,trim ''\n+test_atom tag committeremail:mailmap ''\n+test_atom tag committeremail:mailmap,trim ''\n+test_atom tag committeremail:trim,mailmap ''\n+test_atom tag committeremail:mailmap,localpart ''\n+test_atom tag committeremail:localpart,mailmap ''\n+test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''\n+test_atom tag committerdate ''\n+test_atom tag tag 'testtag'\n+test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n+test_atom tag taggername 'C O Mitter'\n+test_atom tag taggername:mailmap 'C Mitter'\n+test_atom tag taggeremail '<committer@example.com>'\n+test_atom tag taggeremail:trim 'committer@example.com'\n+test_atom tag taggeremail:localpart 'committer'\n+test_atom tag taggeremail:trim,localpart 'committer'\n+test_atom tag taggeremail:mailmap '<cmitter@example.com>'\n+test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'\n+test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'\n+test_atom tag taggeremail:mailmap,localpart 'cmitter'\n+test_atom tag taggeremail:localpart,mailmap 'cmitter'\n+test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'\n+test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n+test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n+test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n+test_atom tag subject 'Tagging at 1151968727'\n+test_atom tag subject:sanitize 'Tagging-at-1151968727'\n+test_atom tag contents:subject 'Tagging at 1151968727'\n+test_atom tag body ''\n+test_atom tag contents:body ''\n+test_atom tag contents:signature ''\n+test_atom tag contents 'Tagging at 1151968727\n+'\n+test_atom tag HEAD ' '\n+\n+test_expect_success 'basic atom: refs/tags/testtag *raw' '\n+\tgit cat-file commit refs/tags/testtag^{} >expected &&\n+\t${git_for_each_ref} --format=\"%(*raw)\" refs/tags/testtag >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_expect_success 'Check invalid atoms names are errors' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(INVALID)\" refs/heads\n+'\n+\n+test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n+\t${git_for_each_ref} --format=\"%(authordate)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n+'\n+\n+test_expect_success 'Check valid format specifiers for date fields' '\n+\t${git_for_each_ref} --format=\"%(authordate:default)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:relative)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:short)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:local)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:iso8601)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:rfc2822)\" refs/heads\n+'\n+\n+test_expect_success 'Check invalid format specifiers are errors' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(authordate:INVALID)\" refs/heads\n+'\n+\n+test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=0)\" &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=-1)\" &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=foo)\"\n+'\n+\n+test_bad_atom () {\n+\tcase \"$1\" in\n+\thead) ref=refs/heads/main ;;\n+\t tag) ref=refs/tags/testtag ;;\n+\t sym) ref=refs/heads/sym ;;\n+\t   *) ref=$1 ;;\n+\tesac\n+\tformat=$2\n+\ttest_do=test_expect_${4:-success}\n+\n+\tprintf '%s\\n' \"$3\" >expect\n+\t$test_do $PREREQ \"err basic atom: $ref $format\" '\n+\t\ttest_must_fail ${git_for_each_ref} \\\n+\t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n+\t\ttest_cmp expect error\n+\t'\n+}\n+\n+test_bad_atom head 'authoremail:foo' \\\n+\t'fatal: unrecognized %(authoremail) argument: foo'\n+\n+test_bad_atom head 'authoremail:mailmap,trim,bar' \\\n+\t'fatal: unrecognized %(authoremail) argument: bar'\n+\n+test_bad_atom head 'authoremail:trim,' \\\n+\t'fatal: unrecognized %(authoremail) argument: '\n+\n+test_bad_atom head 'authoremail:mailmaptrim' \\\n+\t'fatal: unrecognized %(authoremail) argument: trim'\n+\n+test_bad_atom head 'committeremail: ' \\\n+\t'fatal: unrecognized %(committeremail) argument:  '\n+\n+test_bad_atom head 'committeremail: trim,foo' \\\n+\t'fatal: unrecognized %(committeremail) argument:  trim,foo'\n+\n+test_bad_atom head 'committeremail:mailmap,localpart ' \\\n+\t'fatal: unrecognized %(committeremail) argument:  '\n+\n+test_bad_atom head 'committeremail:trim_localpart' \\\n+\t'fatal: unrecognized %(committeremail) argument: _localpart'\n+\n+test_bad_atom head 'committeremail:localpart,,,trim' \\\n+\t'fatal: unrecognized %(committeremail) argument: ,,trim'\n+\n+test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \\\n+\t'fatal: unrecognized %(taggeremail) argument:  foo '\n+\n+test_bad_atom tag 'taggeremail:trim,localpart,' \\\n+\t'fatal: unrecognized %(taggeremail) argument: '\n+\n+test_bad_atom tag 'taggeremail:mailmap;localpart trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument: ;localpart trim'\n+\n+test_bad_atom tag 'taggeremail:localpart trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument:  trim'\n+\n+test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'\n+\n+test_date () {\n+\tf=$1 &&\n+\tcommitter_date=$2 &&\n+\tauthor_date=$3 &&\n+\ttagger_date=$4 &&\n+\tcat >expected <<-EOF &&\n+\t'refs/heads/main' '$committer_date' '$author_date'\n+\t'refs/tags/testtag' '$tagger_date'\n+\tEOF\n+\t(\n+\t\t${git_for_each_ref} --shell \\\n+\t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n+\t\t\trefs/heads &&\n+\t\t${git_for_each_ref} --shell \\\n+\t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n+\t\t\trefs/tags\n+\t) >actual &&\n+\ttest_cmp expected actual\n+}\n+\n+test_expect_success 'Check unformatted date fields output' '\n+\ttest_date \"\" \\\n+\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n+'\n+\n+test_expect_success 'Check format \"default\" formatted date fields output' '\n+\ttest_date default \\\n+\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n+'\n+\n+test_expect_success 'Check format \"default-local\" date fields output' '\n+\ttest_date default-local \"Mon Jul 3 23:18:43 2006\" \"Mon Jul 3 23:18:44 2006\" \"Mon Jul 3 23:18:45 2006\"\n+'\n+\n+# Don't know how to do relative check because I can't know when this script\n+# is going to be run and can't fake the current time to git, and hence can't\n+# provide expected output.  Instead, I'll just make sure that \"relative\"\n+# doesn't exit in error\n+test_expect_success 'Check format \"relative\" date fields output' '\n+\tf=relative &&\n+\t(${git_for_each_ref} --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n+\t${git_for_each_ref} --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n+'\n+\n+# We just check that this is the same as \"relative\" for now.\n+test_expect_success 'Check format \"relative-local\" date fields output' '\n+\ttest_date relative-local \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(committerdate:relative)\" refs/heads)\" \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(authordate:relative)\" refs/heads)\" \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(taggerdate:relative)\" refs/tags)\"\n+'\n+\n+test_expect_success 'Check format \"short\" date fields output' '\n+\ttest_date short 2006-07-04 2006-07-04 2006-07-04\n+'\n+\n+test_expect_success 'Check format \"short-local\" date fields output' '\n+\ttest_date short-local 2006-07-03 2006-07-03 2006-07-03\n+'\n+\n+test_expect_success 'Check format \"local\" date fields output' '\n+\ttest_date local \\\n+\t\t\"Mon Jul 3 23:18:43 2006\" \\\n+\t\t\"Mon Jul 3 23:18:44 2006\" \\\n+\t\t\"Mon Jul 3 23:18:45 2006\"\n+'\n+\n+test_expect_success 'Check format \"iso8601\" date fields output' '\n+\ttest_date iso8601 \\\n+\t\t\"2006-07-04 01:18:43 +0200\" \\\n+\t\t\"2006-07-04 01:18:44 +0200\" \\\n+\t\t\"2006-07-04 01:18:45 +0200\"\n+'\n+\n+test_expect_success 'Check format \"iso8601-local\" date fields output' '\n+\ttest_date iso8601-local \"2006-07-03 23:18:43 +0000\" \"2006-07-03 23:18:44 +0000\" \"2006-07-03 23:18:45 +0000\"\n+'\n+\n+test_expect_success 'Check format \"rfc2822\" date fields output' '\n+\ttest_date rfc2822 \\\n+\t\t\"Tue, 4 Jul 2006 01:18:43 +0200\" \\\n+\t\t\"Tue, 4 Jul 2006 01:18:44 +0200\" \\\n+\t\t\"Tue, 4 Jul 2006 01:18:45 +0200\"\n+'\n+\n+test_expect_success 'Check format \"rfc2822-local\" date fields output' '\n+\ttest_date rfc2822-local \"Mon, 3 Jul 2006 23:18:43 +0000\" \"Mon, 3 Jul 2006 23:18:44 +0000\" \"Mon, 3 Jul 2006 23:18:45 +0000\"\n+'\n+\n+test_expect_success 'Check format \"raw\" date fields output' '\n+\ttest_date raw \"1151968723 +0200\" \"1151968724 +0200\" \"1151968725 +0200\"\n+'\n+\n+test_expect_success 'Check format \"raw-local\" date fields output' '\n+\ttest_date raw-local \"1151968723 +0000\" \"1151968724 +0000\" \"1151968725 +0000\"\n+'\n+\n+test_expect_success 'Check format of strftime date fields' '\n+\techo \"my date is 2006-07-04\" >expected &&\n+\t${git_for_each_ref} \\\n+\t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n+\t  refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check format of strftime-local date fields' '\n+\techo \"my date is 2006-07-03\" >expected &&\n+\t${git_for_each_ref} \\\n+\t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n+\t  refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'exercise strftime with odd fields' '\n+\techo >expected &&\n+\t${git_for_each_ref} --format=\"%(authordate:format:)\" refs/heads >actual &&\n+\ttest_cmp expected actual &&\n+\tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n+\techo $long >expected &&\n+\t${git_for_each_ref} --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/heads/main\n+refs/remotes/origin/main\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'Verify ascending sort' '\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=refname >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/remotes/origin/main\n+refs/heads/main\n+EOF\n+\n+test_expect_success 'Verify descending sort' '\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=-refname >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Give help even with invalid sort atoms' '\n+\ttest_expect_code 129 ${git_for_each_ref} --sort=bogus -h >actual 2>&1 &&\n+\tgrep \"^usage: ${git_for_each_ref}\" actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/tags/testtag-2\n+EOF\n+\n+test_expect_success 'exercise patterns with prefixes' '\n+\tgit tag testtag-2 &&\n+\ttest_when_finished \"git tag -d testtag-2\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/tags/testtag-2\n+EOF\n+\n+test_expect_success 'exercise glob patterns with prefixes' '\n+\tgit tag testtag-2 &&\n+\ttest_when_finished \"git tag -d testtag-2\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/bar\n+refs/tags/baz\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'exercise patterns with prefix exclusions' '\n+\tfor tag in foo/one foo/two foo/three bar baz\n+\tdo\n+\t\tgit tag \"$tag\" || return 1\n+\tdone &&\n+\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/bar\n+refs/tags/baz\n+refs/tags/foo/one\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'exercise patterns with pattern exclusions' '\n+\tfor tag in foo/one foo/two foo/three bar baz\n+\tdo\n+\t\tgit tag \"$tag\" || return 1\n+\tdone &&\n+\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+'refs/heads/main'\n+'refs/remotes/origin/main'\n+'refs/tags/testtag'\n+EOF\n+\n+test_expect_success 'Quoting style: shell' '\n+\t${git_for_each_ref} --shell --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Quoting style: perl' '\n+\t${git_for_each_ref} --perl --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Quoting style: python' '\n+\t${git_for_each_ref} --python --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+\"refs/heads/main\"\n+\"refs/remotes/origin/main\"\n+\"refs/tags/testtag\"\n+EOF\n+\n+test_expect_success 'Quoting style: tcl' '\n+\t${git_for_each_ref} --tcl --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n+\ttest_expect_success \"more than one quoting style: $i\" \"\n+\t\ttest_must_fail ${git_for_each_ref} $i 2>err &&\n+\t\tgrep '^error: more than one quoting style' err\n+\t\"\n+done\n+\n+test_expect_success 'setup for upstream:track[short]' '\n+\ttest_commit two\n+'\n+\n+test_atom head upstream:track '[ahead 1]'\n+test_atom head upstream:trackshort '>'\n+test_atom head upstream:track,nobracket 'ahead 1'\n+test_atom head upstream:nobracket,track 'ahead 1'\n+\n+test_expect_success 'setup for push:track[short]' '\n+\ttest_commit third &&\n+\tgit update-ref refs/remotes/myfork/main main &&\n+\tgit reset main~1\n+'\n+\n+test_atom head push:track '[behind 1]'\n+test_atom head push:trackshort '<'\n+\n+test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:track)\" 2>/dev/null &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:trackshort)\" 2>/dev/null\n+'\n+\n+test_expect_success 'Check that :track[short] works when upstream is invalid' '\n+\tcat >expected <<-\\EOF &&\n+\t[gone]\n+\n+\tEOF\n+\ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n+\tgit config branch.main.merge refs/heads/does-not-exist &&\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n+\t\trefs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check for invalid refname format' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:INVALID)\"\n+'\n+\n+test_expect_success 'set up color tests' '\n+\tcat >expected.color <<-EOF &&\n+\t$(git rev-parse --short refs/heads/main) <GREEN>main<RESET>\n+\t$(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>\n+\t$(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>\n+\t$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>\n+\t$(git rev-parse --short refs/tags/third) <GREEN>third<RESET>\n+\t$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>\n+\tEOF\n+\tsed \"s/<[^>]*>//g\" <expected.color >expected.bare &&\n+\tcolor_format=\"%(objectname:short) %(color:green)%(refname:short)\"\n+'\n+\n+test_expect_success TTY '%(color) shows color with a tty' '\n+\ttest_terminal ${git_for_each_ref} --format=\"$color_format\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\ttest_cmp expected.color actual\n+'\n+\n+test_expect_success '%(color) does not show color without tty' '\n+\tTERM=vt100 ${git_for_each_ref} --format=\"$color_format\" >actual &&\n+\ttest_cmp expected.bare actual\n+'\n+\n+test_expect_success '--color can override tty check' '\n+\t${git_for_each_ref} --color --format=\"$color_format\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\ttest_cmp expected.color actual\n+'\n+\n+test_expect_success 'color.ui=always does not override tty check' '\n+\tgit -c color.ui=always ${git_for_each_ref#git} --format=\"$color_format\" >actual &&\n+\ttest_cmp expected.bare actual\n+'\n+\n+test_expect_success 'setup for describe atom tests' '\n+\tgit init -b master describe-repo &&\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\ttest_commit --no-tag one &&\n+\t\tgit tag tagone &&\n+\n+\t\ttest_commit --no-tag two &&\n+\t\tgit tag -a -m \"tag two\" tagtwo\n+\t)\n+'\n+\n+test_expect_success 'describe atom vs git describe' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t${git_for_each_ref} --format=\"%(objectname)\" \\\n+\t\t\trefs/tags/ >obj &&\n+\t\twhile read hash\n+\t\tdo\n+\t\t\tif desc=$(git describe $hash)\n+\t\t\tthen\n+\t\t\t\t: >expect-contains-good\n+\t\t\telse\n+\t\t\t\t: >expect-contains-bad\n+\t\t\tfi &&\n+\t\t\techo \"$hash $desc\" || return 1\n+\t\tdone <obj >expect &&\n+\t\ttest_path_exists expect-contains-good &&\n+\t\ttest_path_exists expect-contains-bad &&\n+\n+\t\t${git_for_each_ref} --format=\"%(objectname) %(describe)\" \\\n+\t\t\trefs/tags/ >actual 2>err &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest_must_be_empty err\n+\t)\n+'\n+\n+test_expect_success 'describe:tags vs describe --tags' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit describe --tags >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:tags)\" \\\n+\t\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t# Case 1: We have commits between HEAD and the most\n+\t\t#\t  recent tag reachable from it\n+\t\ttest_commit --no-tag file &&\n+\t\tgit describe --abbrev=14 >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:abbrev=14)\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# Make sure the hash used is at least 14 digits long\n+\t\tsed -e \"s/^.*-g\\([0-9a-f]*\\)$/\\1/\" <actual >hexpart &&\n+\t\ttest 15 -le $(wc -c <hexpart) &&\n+\n+\t\t# Case 2: We have a tag at HEAD, describe directly gives\n+\t\t#\t  the name of the tag\n+\t\tgit tag -a -m tagged tagname &&\n+\t\tgit describe --abbrev=14 >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:abbrev=14)\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest tagname = $(cat actual)\n+\t)\n+'\n+\n+test_expect_success 'describe:match=... vs describe --match ...' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit tag -a -m \"tag foo\" tag-foo &&\n+\t\tgit describe --match \"*-foo\" >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:match=\"*-foo\")\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit tag -a -m \"tag bar\" tag-bar &&\n+\t\tgit describe --exclude \"*-bar\" >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:exclude=\"*-bar\")\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'deref with describe atom' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tcat >expect <<-\\EOF &&\n+\n+\t\ttagname\n+\t\ttagname\n+\t\ttagname\n+\n+\t\ttagtwo\n+\t\tEOF\n+\t\t${git_for_each_ref} --format=\"%(*describe)\" >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'err on bad describe atom arg' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t# The bad arg is the only arg passed to describe atom\n+\t\tcat >expect <<-\\EOF &&\n+\t\tfatal: unrecognized %(describe) argument: baz\n+\t\tEOF\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%(describe:baz)\" \\\n+\t\t\trefs/heads/master 2>actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# The bad arg is in the middle of the option string\n+\t\t# passed to the describe atom\n+\t\tcat >expect <<-\\EOF &&\n+\t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n+\t\tEOF\n+\t\ttest_must_fail ${git_for_each_ref} \\\n+\t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n+\t\t\tref/heads/master 2>actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+cat >expected <<\\EOF\n+heads/main\n+tags/main\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs (strict)' '\n+\tgit config --bool core.warnambiguousrefs true &&\n+\tgit checkout -b newtag &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Branch\" &&\n+\tsetdate_and_increment &&\n+\tgit tag -m \"Tagging at $datestamp\" main &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/main\n+main\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs (loose)' '\n+\tgit config --bool core.warnambiguousrefs false &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/ambiguous\n+ambiguous\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n+\tgit checkout main &&\n+\tgit tag ambiguous testtag^0 &&\n+\tgit branch ambiguous testtag^0 &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create tag without tagger' '\n+\tgit tag -a -m \"Broken tag\" taggerless &&\n+\tgit tag -f taggerless $(git cat-file tag taggerless |\n+\t\tsed -e \"/^tagger /d\" |\n+\t\tgit hash-object --literally --stdin -w -t tag)\n+'\n+\n+test_atom refs/tags/taggerless type 'commit'\n+test_atom refs/tags/taggerless tag 'taggerless'\n+test_atom refs/tags/taggerless tagger ''\n+test_atom refs/tags/taggerless taggername ''\n+test_atom refs/tags/taggerless taggeremail ''\n+test_atom refs/tags/taggerless taggeremail:trim ''\n+test_atom refs/tags/taggerless taggeremail:localpart ''\n+test_atom refs/tags/taggerless taggerdate ''\n+test_atom refs/tags/taggerless committer ''\n+test_atom refs/tags/taggerless committername ''\n+test_atom refs/tags/taggerless committeremail ''\n+test_atom refs/tags/taggerless committeremail:trim ''\n+test_atom refs/tags/taggerless committeremail:localpart ''\n+test_atom refs/tags/taggerless committerdate ''\n+test_atom refs/tags/taggerless subject 'Broken tag'\n+\n+test_expect_success 'an unusual tag with an incomplete line' '\n+\n+\tgit tag -m \"bogo\" bogo &&\n+\tbogo=$(git cat-file tag bogo) &&\n+\tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n+\tgit tag -f bogo \"$bogo\" &&\n+\t${git_for_each_ref} --format \"%(body)\" refs/tags/bogo\n+\n+'\n+\n+test_expect_success 'create tag with subject and body content' '\n+\tcat >>msg <<-\\EOF &&\n+\t\tthe subject line\n+\n+\t\tfirst body line\n+\t\tsecond body line\n+\tEOF\n+\tgit tag -F msg subject-body\n+'\n+test_atom refs/tags/subject-body subject 'the subject line'\n+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n+test_atom refs/tags/subject-body body 'first body line\n+second body line\n+'\n+test_atom refs/tags/subject-body contents 'the subject line\n+\n+first body line\n+second body line\n+'\n+\n+test_expect_success 'create tag with multiline subject' '\n+\tcat >msg <<-\\EOF &&\n+\t\tfirst subject line\n+\t\tsecond subject line\n+\n+\t\tfirst body line\n+\t\tsecond body line\n+\tEOF\n+\tgit tag -F msg multiline\n+'\n+test_atom refs/tags/multiline subject 'first subject line second subject line'\n+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n+test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n+test_atom refs/tags/multiline body 'first body line\n+second body line\n+'\n+test_atom refs/tags/multiline contents:body 'first body line\n+second body line\n+'\n+test_atom refs/tags/multiline contents:signature ''\n+test_atom refs/tags/multiline contents 'first subject line\n+second subject line\n+\n+first body line\n+second body line\n+'\n+\n+test_expect_success GPG 'create signed tags' '\n+\tgit tag -s -m \"\" signed-empty &&\n+\tgit tag -s -m \"subject line\" signed-short &&\n+\tcat >msg <<-\\EOF &&\n+\tsubject line\n+\n+\tbody contents\n+\tEOF\n+\tgit tag -s -F msg signed-long\n+'\n+\n+sig='-----BEGIN PGP SIGNATURE-----\n+-----END PGP SIGNATURE-----\n+'\n+\n+PREREQ=GPG\n+test_atom refs/tags/signed-empty subject ''\n+test_atom refs/tags/signed-empty subject:sanitize ''\n+test_atom refs/tags/signed-empty contents:subject ''\n+test_atom refs/tags/signed-empty body \"$sig\"\n+test_atom refs/tags/signed-empty contents:body ''\n+test_atom refs/tags/signed-empty contents:signature \"$sig\"\n+test_atom refs/tags/signed-empty contents \"$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n+\tgit cat-file tag refs/tags/signed-empty >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_atom refs/tags/signed-short subject 'subject line'\n+test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n+test_atom refs/tags/signed-short contents:subject 'subject line'\n+test_atom refs/tags/signed-short body \"$sig\"\n+test_atom refs/tags/signed-short contents:body ''\n+test_atom refs/tags/signed-short contents:signature \"$sig\"\n+test_atom refs/tags/signed-short contents \"subject line\n+$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n+\tgit cat-file tag refs/tags/signed-short >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-short >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_atom refs/tags/signed-long subject 'subject line'\n+test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n+test_atom refs/tags/signed-long contents:subject 'subject line'\n+test_atom refs/tags/signed-long body \"body contents\n+$sig\"\n+test_atom refs/tags/signed-long contents:body 'body contents\n+'\n+test_atom refs/tags/signed-long contents:signature \"$sig\"\n+test_atom refs/tags/signed-long contents \"subject line\n+\n+body contents\n+$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n+\tgit cat-file tag refs/tags/signed-long >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-long >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_expect_success 'set up refs pointing to tree and blob' '\n+\tgit update-ref refs/mytrees/first refs/heads/main^{tree} &&\n+\tgit update-ref refs/myblobs/first refs/heads/main:one\n+'\n+\n+test_atom refs/mytrees/first subject \"\"\n+test_atom refs/mytrees/first contents:subject \"\"\n+test_atom refs/mytrees/first body \"\"\n+test_atom refs/mytrees/first contents:body \"\"\n+test_atom refs/mytrees/first contents:signature \"\"\n+test_atom refs/mytrees/first contents \"\"\n+\n+test_expect_success 'basic atom: refs/mytrees/first raw' '\n+\tgit cat-file tree refs/mytrees/first >expected &&\n+\techo >>expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file -s refs/mytrees/first >expected &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_atom refs/myblobs/first subject \"\"\n+test_atom refs/myblobs/first contents:subject \"\"\n+test_atom refs/myblobs/first body \"\"\n+test_atom refs/myblobs/first contents:body \"\"\n+test_atom refs/myblobs/first contents:signature \"\"\n+test_atom refs/myblobs/first contents \"\"\n+\n+test_expect_success 'basic atom: refs/myblobs/first raw' '\n+\tgit cat-file blob refs/myblobs/first >expected &&\n+\techo >>expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/myblobs/first >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file -s refs/myblobs/first >expected &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'set up refs pointing to binary blob' '\n+\tprintf \"a\\0b\\0c\" >blob1 &&\n+\tprintf \"a\\0c\\0b\" >blob2 &&\n+\tprintf \"\\0a\\0b\\0c\" >blob3 &&\n+\tprintf \"abc\" >blob4 &&\n+\tprintf \"\\0 \\0 \\0 \" >blob5 &&\n+\tprintf \"\\0 \\0a\\0 \" >blob6 &&\n+\tprintf \"  \" >blob7 &&\n+\t>blob8 &&\n+\tobj=$(git hash-object -w blob1) &&\n+\tgit update-ref refs/myblobs/blob1 \"$obj\" &&\n+\tobj=$(git hash-object -w blob2) &&\n+\tgit update-ref refs/myblobs/blob2 \"$obj\" &&\n+\tobj=$(git hash-object -w blob3) &&\n+\tgit update-ref refs/myblobs/blob3 \"$obj\" &&\n+\tobj=$(git hash-object -w blob4) &&\n+\tgit update-ref refs/myblobs/blob4 \"$obj\" &&\n+\tobj=$(git hash-object -w blob5) &&\n+\tgit update-ref refs/myblobs/blob5 \"$obj\" &&\n+\tobj=$(git hash-object -w blob6) &&\n+\tgit update-ref refs/myblobs/blob6 \"$obj\" &&\n+\tobj=$(git hash-object -w blob7) &&\n+\tgit update-ref refs/myblobs/blob7 \"$obj\" &&\n+\tobj=$(git hash-object -w blob8) &&\n+\tgit update-ref refs/myblobs/blob8 \"$obj\"\n+'\n+\n+test_expect_success 'Verify sorts with raw' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob8\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/blob3\n+\trefs/myblobs/blob7\n+\trefs/mytrees/first\n+\trefs/myblobs/first\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob4\n+\trefs/heads/main\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=raw \\\n+\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Verify sorts with raw:size' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob8\n+\trefs/myblobs/blob7\n+\trefs/myblobs/blob4\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob3\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/first\n+\trefs/mytrees/first\n+\trefs/heads/main\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=raw:size \\\n+\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'validate raw atom with %(if:equals)' '\n+\tcat >expected <<-EOF &&\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\trefs/myblobs/blob4\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n+\t\trefs/myblobs/ refs/heads/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'validate raw atom with %(if:notequals)' '\n+\tcat >expected <<-EOF &&\n+\trefs/heads/ambiguous\n+\trefs/heads/main\n+\trefs/heads/newtag\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob3\n+\tequals\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/blob7\n+\trefs/myblobs/blob8\n+\trefs/myblobs/first\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n+\t\trefs/myblobs/ refs/heads/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'empty raw refs with %(if)' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob1 not empty\n+\trefs/myblobs/blob2 not empty\n+\trefs/myblobs/blob3 not empty\n+\trefs/myblobs/blob4 not empty\n+\trefs/myblobs/blob5 not empty\n+\trefs/myblobs/blob6 not empty\n+\trefs/myblobs/blob7 empty\n+\trefs/myblobs/blob8 empty\n+\trefs/myblobs/first not empty\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n+\t\trefs/myblobs/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '%(raw) with --python must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --python\n+'\n+\n+test_expect_success '%(raw) with --tcl must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --tcl\n+'\n+\n+test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n+\tcmp blob1 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n+\tcmp blob3 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n+\tcmp blob8 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n+\tcmp one actual &&\n+\tgit cat-file tree refs/mytrees/first > expected &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n+\tcmp expected actual\n+'\n+\n+test_expect_success '%(raw) with --shell must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --shell\n+'\n+\n+test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --sort=raw --shell\n+'\n+\n+test_expect_success '%(raw:size) with --shell' '\n+\t${git_for_each_ref} --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" --shell >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --format compare with cat-file --batch\" '\n+\tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n+\t${git_for_each_ref} --format=\"%(objectname) %(objecttype) %(objectsize)\n+%(raw)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify sorts with contents:size' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/main\n+\trefs/heads/newtag\n+\trefs/heads/ambiguous\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\t--sort=contents:size refs/heads/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'set up multiple-sort tags' '\n+\tfor when in 100000 200000\n+\tdo\n+\t\tfor email in user1 user2\n+\t\tdo\n+\t\t\tfor ref in ref1 ref2\n+\t\t\tdo\n+\t\t\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n+\t\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n+\t\t\t\tgit tag -m \"tag $ref-$when-$email\" \\\n+\t\t\t\tmulti-$ref-$when-$email || return 1\n+\t\t\tdone\n+\t\tdone\n+\tdone\n+'\n+\n+test_expect_success 'Verify sort with multiple keys' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=-refname \\\n+\t\t--sort=taggeremail \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'equivalent sorts fall back on refname' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--no-sort cancels the previous sort keys' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=-refname \\\n+\t\t--sort=taggeremail \\\n+\t\t--no-sort \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n+\tcat >expected <<-\\EOF &&\n+\trefs/tags/multi-ref1-100000-user1\n+\trefs/tags/multi-ref1-100000-user2\n+\trefs/tags/multi-ref1-200000-user1\n+\trefs/tags/multi-ref1-200000-user2\n+\trefs/tags/multi-ref2-100000-user1\n+\trefs/tags/multi-ref2-100000-user2\n+\trefs/tags/multi-ref2-200000-user1\n+\trefs/tags/multi-ref2-200000-user2\n+\tEOF\n+\n+\t# Sort the results with `sort` for a consistent comparison against\n+\t# expected\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(refname)\" \\\n+\t\t--no-sort \\\n+\t\t\"refs/tags/multi-*\" | sort >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'set up custom date sorting' '\n+\t# Dates:\n+\t# - Wed Feb 07 2024 21:34:20 +0000\n+\t# - Tue Dec 14 1999 00:05:22 +0000\n+\t# - Fri Jun 04 2021 11:26:51 +0000\n+\t# - Mon Jan 22 2007 16:44:01 GMT+0000\n+\ti=1 &&\n+\tfor when in 1707341660 945129922 1622806011 1169484241\n+\tdo\n+\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n+\t\tGIT_COMMITTER_EMAIL=\"user@example.com\" \\\n+\t\tgit tag -m \"tag $when\" custom-dates-$i &&\n+\t\ti=$(($i+1)) || return 1\n+\tdone\n+'\n+\n+test_expect_success 'sort by date defaults to full timestamp' '\n+\tcat >expected <<-\\EOF &&\n+\t945129922 refs/tags/custom-dates-2\n+\t1169484241 refs/tags/custom-dates-4\n+\t1622806011 refs/tags/custom-dates-3\n+\t1707341660 refs/tags/custom-dates-1\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(creatordate:unix) %(refname)\" \\\n+\t\t--sort=creatordate \\\n+\t\t\"refs/tags/custom-dates-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'sort by custom date format' '\n+\tcat >expected <<-\\EOF &&\n+\t00:05:22 refs/tags/custom-dates-2\n+\t11:26:51 refs/tags/custom-dates-3\n+\t16:44:01 refs/tags/custom-dates-4\n+\t21:34:20 refs/tags/custom-dates-1\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n+\t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n+\t\t\"refs/tags/custom-dates-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n+\ttest_when_finished \"git checkout main\" &&\n+\t${git_for_each_ref} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\tsed -e \"s/^\\* /  /\" actual >expect &&\n+\tgit checkout --orphan orphaned-branch &&\n+\t${git_for_each_ref} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+cat >trailers <<EOF\n+Reviewed-by: A U Thor <author@example.com>\n+Signed-off-by: A U Thor <author@example.com>\n+[ v2 updated patch description ]\n+Acked-by: A U Thor\n+  <author@example.com>\n+EOF\n+\n+unfold () {\n+\tperl -0pe 's/\\n\\s+/ /g'\n+}\n+\n+test_expect_success 'set up trailers for next test' '\n+\techo \"Some contents\" > two &&\n+\tgit add two &&\n+\tgit commit -F - <<-EOF\n+\ttrailers: this commit message has trailers\n+\n+\tSome message contents\n+\n+\t$(cat trailers)\n+\tEOF\n+'\n+\n+test_trailer_option () {\n+\tif test \"$#\" -eq 3\n+\tthen\n+\t\tprereq=\"$1\"\n+\t\tshift\n+\tfi &&\n+\ttitle=$1 option=$2\n+\tcat >expect\n+\ttest_expect_success $prereq \"$title\" '\n+\t\t${git_for_each_ref} --format=\"%($option)\" refs/heads/main >actual &&\n+\t\ttest_cmp expect actual &&\n+\t\t${git_for_each_ref} --format=\"%(contents:$option)\" refs/heads/main >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \\\n+\t'trailers:unfold' <<-EOF\n+\t$(unfold <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only) shows only \"key: value\" trailers' \\\n+\t'trailers:only' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=no,only=true) shows only \"key: value\" trailers' \\\n+\t'trailers:only=no,only=true' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=yes) shows only \"key: value\" trailers' \\\n+\t'trailers:only=yes' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=no) shows all trailers' \\\n+\t'trailers:only=no' <<-EOF\n+\t$(cat trailers)\n+\n+\tEOF\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \\\n+\t'trailers:only,unfold' <<-EOF\n+\t$(grep -v patch.description <trailers | unfold)\n+\n+\tEOF\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \\\n+\t'trailers:unfold,only' <<-EOF\n+\t$(grep -v patch.description <trailers | unfold)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) shows that trailer' \\\n+\t'trailers:key=Signed-off-by' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) is case insensitive' \\\n+\t'trailers:key=SiGned-oFf-bY' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo:) trailing colon also works' \\\n+\t'trailers:key=Signed-off-by:' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) multiple keys' \\\n+\t'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF\n+\tReviewed-by: A U Thor <author@example.com>\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=nonexistent) becomes empty' \\\n+\t'trailers:key=Shined-off-by:' <<-EOF\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \\\n+\t'trailers:key=Acked-by' <<-EOF\n+\t$(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \\\n+\t'trailers:key=Signed-Off-by,unfold' <<-EOF\n+\t$(unfold <trailers | grep Signed-off-by)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \\\n+\t'trailers:key=Signed-off-by,only=no' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\t$(grep patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \\\n+\t'trailers:key=Signed-off-by,valueonly' <<-EOF\n+\tA U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:separator) changes separator' \\\n+\t'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>\n+\tEOF\n+\n+test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \\\n+\t'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by,A U Thor <author@example.com>\n+\tSigned-off-by,A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \\\n+\t'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>\n+\tEOF\n+\n+test_expect_success 'multiple %(trailers) use their own options' '\n+\tgit tag -F - tag-with-trailers <<-\\EOF &&\n+\tbody\n+\n+\tone: foo\n+\tone: bar\n+\ttwo: baz\n+\ttwo: qux\n+\tEOF\n+\tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n+\tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n+\t${git_for_each_ref} --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n+\tcat >expect <<-\\EOF &&\n+\toneWfooXoneWbar\n+\ttwoYbazZtwoYqux\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_failing_trailer_option () {\n+\ttitle=$1 option=$2\n+\tcat >expect\n+\ttest_expect_success \"$title\" '\n+\t\t# error message cannot be checked under i18n\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%($option)\" refs/heads/main 2>actual &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \\\n+\t'trailers:unsupported' <<-\\EOF\n+\tfatal: unknown %(trailers) argument: unsupported\n+\tEOF\n+\n+test_failing_trailer_option '%(trailers:key) without value is error' \\\n+\t'trailers:key' <<-\\EOF\n+\tfatal: expected %(trailers:key=<value>)\n+\tEOF\n+\n+test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '\n+\tcat >expect <<-EOF &&\n+\tfatal: unrecognized %(contents) argument: trailersonly\n+\tEOF\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(contents:trailersonly)\" 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'basic atom: head contents:trailers' '\n+\t${git_for_each_ref} --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\t# ${git_for_each_ref} ends with a blank line\n+\tcat >expect <<-EOF &&\n+\t$(cat trailers)\n+\n+\tEOF\n+\ttest_cmp expect actual.clean\n+'\n+\n+test_expect_success 'basic atom: rest must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(rest)\" refs/heads/main\n+'\n+\n+test_expect_success 'HEAD atom does not take arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(HEAD:foo)\" 2>err &&\n+\techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'subject atom rejects unknown arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(subject:foo)\" 2>err &&\n+\techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'refname atom rejects unknown arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:foo)\" 2>err &&\n+\techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'trailer parsing not fooled by --- line' '\n+\tgit commit --allow-empty -F - <<-\\EOF &&\n+\tthis is the subject\n+\n+\tThis is the body. The message has a \"---\" line which would confuse a\n+\tmessage+patch parser. But here we know we have only a commit message,\n+\tso we get it right.\n+\n+\ttrailer: wrong\n+\t---\n+\tThis is more body.\n+\n+\ttrailer: right\n+\tEOF\n+\n+\t{\n+\t\techo \"trailer: right\" &&\n+\t\techo\n+\t} >expect &&\n+\t${git_for_each_ref} --format=\"%(trailers)\" refs/heads/main >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'Add symbolic ref for the following tests' '\n+\tgit symbolic-ref refs/heads/sym refs/heads/main\n+'\n+\n+cat >expected <<EOF\n+refs/heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref) atom' '\n+\t${git_for_each_ref} --format=\"%(symref)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:short) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:short)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+main\n+heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual &&\n+\n+\t${git_for_each_ref} --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+refs\n+refs/heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success ':remotename and :remoteref' '\n+\tgit init remote-tests &&\n+\t(\n+\t\tcd remote-tests &&\n+\t\ttest_commit initial &&\n+\t\tgit branch -M main &&\n+\t\tgit remote add from fifth.coffee:blub &&\n+\t\tgit config branch.main.remote from &&\n+\t\tgit config branch.main.merge refs/heads/stable &&\n+\t\tgit remote add to southridge.audio:repo &&\n+\t\tgit config remote.to.push \"refs/heads/*:refs/heads/pushed/*\" &&\n+\t\tgit config branch.main.pushRemote to &&\n+\t\tfor pair in \"%(upstream)=refs/remotes/from/stable\" \\\n+\t\t\t\"%(upstream:remotename)=from\" \\\n+\t\t\t\"%(upstream:remoteref)=refs/heads/stable\" \\\n+\t\t\t\"%(push)=refs/remotes/to/pushed/main\" \\\n+\t\t\t\"%(push:remotename)=to\" \\\n+\t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n+\t\tdo\n+\t\t\techo \"${pair#*=}\" >expect &&\n+\t\t\t${git_for_each_ref} --format=\"${pair%=*}\" \\\n+\t\t\t\trefs/heads/main >actual &&\n+\t\t\ttest_cmp expect actual || exit 1\n+\t\tdone &&\n+\t\tgit branch push-simple &&\n+\t\tgit config branch.push-simple.pushRemote from &&\n+\t\tactual=\"$(${git_for_each_ref} \\\n+\t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n+\t\t\trefs/heads/push-simple)\" &&\n+\t\ttest from, = \"$actual\"\n+\t)\n+'\n+\n+test_expect_success \"${git_for_each_ref} --ignore-case ignores case\" '\n+\t${git_for_each_ref} --format=\"%(refname)\" refs/heads/MAIN >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\techo refs/heads/main >expect &&\n+\t${git_for_each_ref} --format=\"%(refname)\" --ignore-case \\\n+\t\trefs/heads/MAIN >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --omit-empty works\" '\n+\t${git_for_each_ref} --format=\"%(refname)\" >actual &&\n+\ttest_line_count -gt 1 actual &&\n+\t${git_for_each_ref} --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n+\techo refs/heads/main >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --ignore-case works on multiple sort keys\" '\n+\t# name refs numerically to avoid case-insensitive filesystem conflicts\n+\tnr=0 &&\n+\tfor email in a A b B\n+\tdo\n+\t\tfor subject in a A b B\n+\t\tdo\n+\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n+\t\t\tgit tag -m \"tag $subject\" icase-$(printf %02d $nr) &&\n+\t\t\tnr=$((nr+1))||\n+\t\t\treturn 1\n+\t\tdone\n+\tdone &&\n+\t${git_for_each_ref} --ignore-case \\\n+\t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n+\t\t--sort=refname \\\n+\t\t--sort=subject \\\n+\t\t--sort=taggeremail \\\n+\t\trefs/tags/icase-* >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t<a@example.com> tag a refs/tags/icase-00\n+\t<a@example.com> tag A refs/tags/icase-01\n+\t<A@example.com> tag a refs/tags/icase-04\n+\t<A@example.com> tag A refs/tags/icase-05\n+\t<a@example.com> tag b refs/tags/icase-02\n+\t<a@example.com> tag B refs/tags/icase-03\n+\t<A@example.com> tag b refs/tags/icase-06\n+\t<A@example.com> tag B refs/tags/icase-07\n+\t<b@example.com> tag a refs/tags/icase-08\n+\t<b@example.com> tag A refs/tags/icase-09\n+\t<B@example.com> tag a refs/tags/icase-12\n+\t<B@example.com> tag A refs/tags/icase-13\n+\t<b@example.com> tag b refs/tags/icase-10\n+\t<b@example.com> tag B refs/tags/icase-11\n+\t<B@example.com> tag b refs/tags/icase-14\n+\t<B@example.com> tag B refs/tags/icase-15\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} reports broken tags\" '\n+\tgit tag -m \"good tag\" broken-tag-good HEAD &&\n+\tgit cat-file tag broken-tag-good >good &&\n+\tsed s/commit/blob/ <good >bad &&\n+\tbad=$(git hash-object -w -t tag bad) &&\n+\tgit update-ref refs/tags/broken-tag-bad $bad &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(*objectname)\" \\\n+\t\trefs/tags/broken-tag-*\n+'\n+\n+test_expect_success 'set up tag with signature and no blank lines' '\n+\tgit tag -F - fake-sig-no-blanks <<-\\EOF\n+\tthis is the subject\n+\t-----BEGIN PGP SIGNATURE-----\n+\tnot a real signature, but we just care about the\n+\tsubject/body parsing. It is important here that\n+\tthere are no blank lines in the signature.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+'\n+\n+test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'\n+test_atom refs/tags/fake-sig-no-blanks contents:body ''\n+test_atom refs/tags/fake-sig-no-blanks contents:signature \"$sig\"\n+\n+test_expect_success 'set up tag with CRLF signature' '\n+\tappend_cr <<-\\EOF |\n+\tthis is the subject\n+\t-----BEGIN PGP SIGNATURE-----\n+\n+\tnot a real signature, but we just care about\n+\tthe subject/body parsing. It is important here\n+\tthat there is a blank line separating this\n+\tfrom the signature header.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+\tgit tag -F - --cleanup=verbatim fake-sig-crlf\n+'\n+\n+test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'\n+test_atom refs/tags/fake-sig-crlf contents:body ''\n+\n+# CRLF is retained in the signature, so we have to pass our expected value\n+# through append_cr. But test_atom requires a shell string, which means command\n+# substitution, and the shell will strip trailing newlines from the output of\n+# the substitution. Hack around it by adding and then removing a dummy line.\n+sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n+sig_crlf=${sig_crlf%dummy}\n+test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n+\n+test_expect_success 'set up tag with signature and trailers' '\n+\tgit tag -F - fake-sig-trailer <<-\\EOF\n+\tthis is the subject\n+\n+\tthis is the body\n+\n+\tMy-Trailer: foo\n+\t-----BEGIN PGP SIGNATURE-----\n+\n+\tnot a real signature, but we just care about the\n+\tsubject/body/trailer parsing.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+'\n+\n+# use \"separator=\" here to suppress the terminating newline\n+test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: empty\" '\n+\t>in &&\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\t${git_for_each_ref} --format=\"%(refname)\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: fails if extra args\" '\n+\t>in &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\t--stdin refs/heads/extra <in 2>err &&\n+\tgrep \"unknown arguments supplied with --stdin\" err\n+'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: matches\" '\n+\tcat >in <<-EOF &&\n+\trefs/tags/multi*\n+\trefs/heads/amb*\n+\tEOF\n+\n+\tcat >expect <<-EOF &&\n+\trefs/heads/ambiguous\n+\trefs/tags/multi-ref1-100000-user1\n+\trefs/tags/multi-ref1-100000-user2\n+\trefs/tags/multi-ref1-200000-user1\n+\trefs/tags/multi-ref1-200000-user2\n+\trefs/tags/multi-ref2-100000-user1\n+\trefs/tags/multi-ref2-100000-user2\n+\trefs/tags/multi-ref2-200000-user1\n+\trefs/tags/multi-ref2-200000-user2\n+\trefs/tags/multiline\n+\tEOF\n+\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} with non-existing refs\" '\n+\tcat >in <<-EOF &&\n+\trefs/heads/this-ref-does-not-exist\n+\trefs/tags/bogus\n+\tEOF\n+\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\txargs ${git_for_each_ref} --format=\"%(refname)\" <in >actual &&\n+\ttest_must_be_empty actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} with nested tags\" '\n+\tgit tag -am \"Normal tag\" nested/base HEAD &&\n+\tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n+\tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n+\n+\thead_oid=\"$(git rev-parse HEAD)\" &&\n+\tbase_tag_oid=\"$(git rev-parse refs/tags/nested/base)\" &&\n+\tnest1_tag_oid=\"$(git rev-parse refs/tags/nested/nest1)\" &&\n+\tnest2_tag_oid=\"$(git rev-parse refs/tags/nested/nest2)\" &&\n+\n+\tcat >expect <<-EOF &&\n+\trefs/tags/nested/base $base_tag_oid tag $head_oid commit\n+\trefs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit\n+\trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n+\t\trefs/tags/nested/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'is-base atom with non-commits' '\n+\t${git_for_each_ref} --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n+\tgrep \"(HEAD) refs/heads/main\" out &&\n+\n+\ttest_line_count = 2 err &&\n+\tgrep \"error: object .* is a commit, not a blob\" err &&\n+\tgrep \"error: bad tag pointer to\" err\n+'\n+\n+GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+\n+test_expect_success GPG 'setup for signature atom using gpg' '\n+\tgit checkout -b signed &&\n+\n+\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n+\n+\techo \"1\" >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -S -m \"file: 1\" &&\n+\tgit tag first-signed &&\n+\n+\techo \"2\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -m \"file: 2\" &&\n+\tgit tag second-unsigned &&\n+\n+\tgit config commit.gpgSign 1 &&\n+\techo \"3\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n+\tgit tag third-unsigned &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n+\tgit tag third-signed &&\n+\n+\techo \"4\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -SB7227189 -m \"file: 4\" &&\n+\tgit tag fourth-signed &&\n+\n+\techo \"5\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n+\tgit tag fifth-unsigned &&\n+\n+\techo \"6\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ &&\n+\tgit tag fifth-signed HEAD^ &&\n+\tgit tag sixth-signed &&\n+\n+\techo \"7\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n+\tgit tag seventh-unsigned\n+'\n+\n+test_expect_success GPGSSH 'setup for signature atom using ssh' '\n+\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n+\n+\ttest_config gpg.format ssh &&\n+\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n+\techo \"8\" >file &&\n+\ttest_tick &&\n+\tgit add file &&\n+\tgit commit -S -m \"file: 8\" &&\n+\tgit tag eighth-signed-ssh\n+'\n+\n+test_expect_success GPG2 'bare signature atom' '\n+\tgit verify-commit first-signed 2>expect &&\n+\techo  >>expect &&\n+\t${git_for_each_ref} refs/tags/first-signed \\\n+\t\t--format=\"%(signature)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show good signature with custom format' '\n+\tgit verify-commit first-signed &&\n+\tcat >expect <<-\\EOF &&\n+\tG\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\t${git_for_each_ref} refs/tags/first-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n+\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n+\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n+\tcat >expect.tmpl <<-\\EOF &&\n+\tG\n+\tFINGERPRINT\n+\tprincipal with number 1\n+\tFINGERPRINT\n+\n+\tEOF\n+\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n+\t${git_for_each_ref} refs/tags/eighth-signed-ssh \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'signature atom with grade option and bad signature' '\n+\tgit cat-file commit third-signed >raw &&\n+\tsed -e \"s/^file: 3/file: 3 forged/\" raw >forged1 &&\n+\tFORGED1=$(git hash-object -w -t commit forged1) &&\n+\tgit update-ref refs/tags/third-signed \"$FORGED1\" &&\n+\ttest_must_fail git verify-commit \"$FORGED1\" &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tB\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\n+\n+\tEOF\n+\t${git_for_each_ref} refs/tags/third-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tU\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\t${git_for_each_ref} refs/tags/fourth-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with undefined trust level' '\n+\tcat >expect <<-\\EOF &&\n+\tundefined\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\t${git_for_each_ref} refs/tags/fourth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n+\tcat >expect <<-\\EOF &&\n+\tultimate\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\t${git_for_each_ref} refs/tags/sixth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show unknown signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tE\n+\t13B6F51ECDDE430D\n+\n+\n+\n+\tEOF\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" ${git_for_each_ref} \\\n+\t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show lack of signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tN\n+\n+\n+\n+\n+\tEOF\n+\t${git_for_each_ref} refs/tags/seventh-unsigned \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex ce9af79ab1..1d9809114d 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -6,2150 +6,14 @@\n test_description='for-each-ref test'\n \n . ./test-lib.sh\n-GNUPGHOME_NOT_USED=$GNUPGHOME\n-. \"$TEST_DIRECTORY\"/lib-gpg.sh\n-. \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n-# Mon Jul 3 23:18:43 2006 +0000\n-datestamp=1151968723\n-setdate_and_increment () {\n-    GIT_COMMITTER_DATE=\"$datestamp +0200\"\n-    datestamp=$(expr \"$datestamp\" + 1)\n-    GIT_AUTHOR_DATE=\"$datestamp +0200\"\n-    datestamp=$(expr \"$datestamp\" + 1)\n-    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n-}\n-\n-test_object_file_size () {\n-\toid=$(git rev-parse \"$1\")\n-\tpath=\".git/objects/$(test_oid_to_path $oid)\"\n-\ttest_file_size \"$path\"\n-}\n-\n-test_expect_success setup '\n-\t# setup .mailmap\n-\tcat >.mailmap <<-EOF &&\n-\tA Thor <athor@example.com> A U Thor <author@example.com>\n-\tC Mitter <cmitter@example.com> C O Mitter <committer@example.com>\n-\tEOF\n-\n-\tsetdate_and_increment &&\n-\techo \"Using $datestamp\" > one &&\n-\tgit add one &&\n-\tgit commit -m \"Initial\" &&\n-\tgit branch -M main &&\n-\tsetdate_and_increment &&\n-\tgit tag -a -m \"Tagging at $datestamp\" testtag &&\n-\tgit update-ref refs/remotes/origin/main main &&\n-\tgit remote add origin nowhere &&\n-\tgit config branch.main.remote origin &&\n-\tgit config branch.main.merge refs/heads/main &&\n-\tgit remote add myfork elsewhere &&\n-\tgit config remote.pushdefault myfork &&\n-\tgit config push.default current\n-'\n-\n-test_atom () {\n-\tcase \"$1\" in\n-\t\thead) ref=refs/heads/main ;;\n-\t\t tag) ref=refs/tags/testtag ;;\n-\t\t sym) ref=refs/heads/sym ;;\n-\t\t   *) ref=$1 ;;\n-\tesac\n-\tformat=$2\n-\ttest_do=test_expect_${4:-success}\n-\n-\tprintf '%s\\n' \"$3\" >expected\n-\t$test_do $PREREQ \"basic atom: $ref $format\" '\n-\t\tgit for-each-ref --format=\"%($format)\" \"$ref\" >actual &&\n-\t\tsanitize_pgp <actual >actual.clean &&\n-\t\ttest_cmp expected actual.clean\n-\t'\n-\n-\t# Automatically test \"contents:size\" atom after testing \"contents\"\n-\tif test \"$format\" = \"contents\"\n-\tthen\n-\t\t# for commit leg, $3 is changed there\n-\t\texpect=$(printf '%s' \"$3\" | wc -c)\n-\t\t$test_do $PREREQ \"basic atom: $ref contents:size\" '\n-\t\t\ttype=$(git cat-file -t \"$ref\") &&\n-\t\t\tcase $type in\n-\t\t\ttag)\n-\t\t\t\t# We cannot use $3 as it expects sanitize_pgp to run\n-\t\t\t\tgit cat-file tag $ref >out &&\n-\t\t\t\texpect=$(tail -n +6 out | wc -c) &&\n-\t\t\t\trm -f out ;;\n-\t\t\ttree | blob)\n-\t\t\t\texpect=\"\" ;;\n-\t\t\tcommit)\n-\t\t\t\t: \"use the calculated expect\" ;;\n-\t\t\t*)\n-\t\t\t\tBUG \"unknown object type\" ;;\n-\t\t\tesac &&\n-\t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n-\t\t\techo $expect >expected &&\n-\t\t\tgit for-each-ref --format=\"%(contents:size)\" \"$ref\" >actual &&\n-\t\t\ttest_cmp expected actual\n-\t\t'\n-\tfi\n-}\n-\n-hexlen=$(test_oid hexsz)\n-\n-test_atom head refname refs/heads/main\n-test_atom head refname: refs/heads/main\n-test_atom head refname:short main\n-test_atom head refname:lstrip=1 heads/main\n-test_atom head refname:lstrip=2 main\n-test_atom head refname:lstrip=-1 main\n-test_atom head refname:lstrip=-2 heads/main\n-test_atom head refname:rstrip=1 refs/heads\n-test_atom head refname:rstrip=2 refs\n-test_atom head refname:rstrip=-1 refs\n-test_atom head refname:rstrip=-2 refs/heads\n-test_atom head refname:strip=1 heads/main\n-test_atom head refname:strip=2 main\n-test_atom head refname:strip=-1 main\n-test_atom head refname:strip=-2 heads/main\n-test_atom head upstream refs/remotes/origin/main\n-test_atom head upstream:short origin/main\n-test_atom head upstream:lstrip=2 origin/main\n-test_atom head upstream:lstrip=-2 origin/main\n-test_atom head upstream:rstrip=2 refs/remotes\n-test_atom head upstream:rstrip=-2 refs/remotes\n-test_atom head upstream:strip=2 origin/main\n-test_atom head upstream:strip=-2 origin/main\n-test_atom head push refs/remotes/myfork/main\n-test_atom head push:short myfork/main\n-test_atom head push:lstrip=1 remotes/myfork/main\n-test_atom head push:lstrip=-1 main\n-test_atom head push:rstrip=1 refs/remotes/myfork\n-test_atom head push:rstrip=-1 refs\n-test_atom head push:strip=1 remotes/myfork/main\n-test_atom head push:strip=-1 main\n-test_atom head objecttype commit\n-test_atom head objectsize $((131 + hexlen))\n-test_atom head objectsize:disk $(test_object_file_size refs/heads/main)\n-test_atom head deltabase $ZERO_OID\n-test_atom head objectname $(git rev-parse refs/heads/main)\n-test_atom head objectname:short $(git rev-parse --short refs/heads/main)\n-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n-test_atom head tree $(git rev-parse refs/heads/main^{tree})\n-test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})\n-test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})\n-test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})\n-test_atom head parent ''\n-test_atom head parent:short ''\n-test_atom head parent:short=1 ''\n-test_atom head parent:short=10 ''\n-test_atom head numparent 0\n-test_atom head object ''\n-test_atom head type ''\n-test_atom head raw \"$(git cat-file commit refs/heads/main)\n-\"\n-test_atom head '*objectname' ''\n-test_atom head '*objecttype' ''\n-test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n-test_atom head authorname 'A U Thor'\n-test_atom head authorname:mailmap 'A Thor'\n-test_atom head authoremail '<author@example.com>'\n-test_atom head authoremail:trim 'author@example.com'\n-test_atom head authoremail:localpart 'author'\n-test_atom head authoremail:trim,localpart 'author'\n-test_atom head authoremail:mailmap '<athor@example.com>'\n-test_atom head authoremail:mailmap,trim 'athor@example.com'\n-test_atom head authoremail:trim,mailmap 'athor@example.com'\n-test_atom head authoremail:mailmap,localpart 'athor'\n-test_atom head authoremail:localpart,mailmap 'athor'\n-test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'\n-test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n-test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n-test_atom head committername 'C O Mitter'\n-test_atom head committername:mailmap 'C Mitter'\n-test_atom head committeremail '<committer@example.com>'\n-test_atom head committeremail:trim 'committer@example.com'\n-test_atom head committeremail:localpart 'committer'\n-test_atom head committeremail:localpart,trim 'committer'\n-test_atom head committeremail:mailmap '<cmitter@example.com>'\n-test_atom head committeremail:mailmap,trim 'cmitter@example.com'\n-test_atom head committeremail:trim,mailmap 'cmitter@example.com'\n-test_atom head committeremail:mailmap,localpart 'cmitter'\n-test_atom head committeremail:localpart,mailmap 'cmitter'\n-test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'\n-test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n-test_atom head tag ''\n-test_atom head tagger ''\n-test_atom head taggername ''\n-test_atom head taggeremail ''\n-test_atom head taggeremail:trim ''\n-test_atom head taggeremail:localpart ''\n-test_atom head taggerdate ''\n-test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n-test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n-test_atom head subject 'Initial'\n-test_atom head subject:sanitize 'Initial'\n-test_atom head contents:subject 'Initial'\n-test_atom head body ''\n-test_atom head contents:body ''\n-test_atom head contents:signature ''\n-test_atom head contents 'Initial\n-'\n-test_atom head HEAD '*'\n-\n-test_atom tag refname refs/tags/testtag\n-test_atom tag refname:short testtag\n-test_atom tag upstream ''\n-test_atom tag push ''\n-test_atom tag objecttype tag\n-test_atom tag objectsize $((114 + hexlen))\n-test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)\n-test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)\n-test_atom tag deltabase $ZERO_OID\n-test_atom tag '*deltabase' $ZERO_OID\n-test_atom tag objectname $(git rev-parse refs/tags/testtag)\n-test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n-test_atom tag tree ''\n-test_atom tag tree:short ''\n-test_atom tag tree:short=1 ''\n-test_atom tag tree:short=10 ''\n-test_atom tag parent ''\n-test_atom tag parent:short ''\n-test_atom tag parent:short=1 ''\n-test_atom tag parent:short=10 ''\n-test_atom tag numparent ''\n-test_atom tag object $(git rev-parse refs/tags/testtag^0)\n-test_atom tag type 'commit'\n-test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})\n-test_atom tag '*objecttype' 'commit'\n-test_atom tag author ''\n-test_atom tag authorname ''\n-test_atom tag authorname:mailmap ''\n-test_atom tag authoremail ''\n-test_atom tag authoremail:trim ''\n-test_atom tag authoremail:localpart ''\n-test_atom tag authoremail:trim,localpart ''\n-test_atom tag authoremail:mailmap ''\n-test_atom tag authoremail:mailmap,trim ''\n-test_atom tag authoremail:trim,mailmap ''\n-test_atom tag authoremail:mailmap,localpart ''\n-test_atom tag authoremail:localpart,mailmap ''\n-test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''\n-test_atom tag authordate ''\n-test_atom tag committer ''\n-test_atom tag committername ''\n-test_atom tag committername:mailmap ''\n-test_atom tag committeremail ''\n-test_atom tag committeremail:trim ''\n-test_atom tag committeremail:localpart ''\n-test_atom tag committeremail:localpart,trim ''\n-test_atom tag committeremail:mailmap ''\n-test_atom tag committeremail:mailmap,trim ''\n-test_atom tag committeremail:trim,mailmap ''\n-test_atom tag committeremail:mailmap,localpart ''\n-test_atom tag committeremail:localpart,mailmap ''\n-test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''\n-test_atom tag committerdate ''\n-test_atom tag tag 'testtag'\n-test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n-test_atom tag taggername 'C O Mitter'\n-test_atom tag taggername:mailmap 'C Mitter'\n-test_atom tag taggeremail '<committer@example.com>'\n-test_atom tag taggeremail:trim 'committer@example.com'\n-test_atom tag taggeremail:localpart 'committer'\n-test_atom tag taggeremail:trim,localpart 'committer'\n-test_atom tag taggeremail:mailmap '<cmitter@example.com>'\n-test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'\n-test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'\n-test_atom tag taggeremail:mailmap,localpart 'cmitter'\n-test_atom tag taggeremail:localpart,mailmap 'cmitter'\n-test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'\n-test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n-test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n-test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n-test_atom tag subject 'Tagging at 1151968727'\n-test_atom tag subject:sanitize 'Tagging-at-1151968727'\n-test_atom tag contents:subject 'Tagging at 1151968727'\n-test_atom tag body ''\n-test_atom tag contents:body ''\n-test_atom tag contents:signature ''\n-test_atom tag contents 'Tagging at 1151968727\n-'\n-test_atom tag HEAD ' '\n-\n-test_expect_success 'basic atom: refs/tags/testtag *raw' '\n-\tgit cat-file commit refs/tags/testtag^{} >expected &&\n-\tgit for-each-ref --format=\"%(*raw)\" refs/tags/testtag >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_expect_success 'Check invalid atoms names are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'for-each-ref does not crash with -h' '\n+test_expect_success \"for-each-ref does not crash with -h\" '\n \ttest_expect_code 129 git for-each-ref -h >usage &&\n \ttest_grep \"[Uu]sage: git for-each-ref \" usage &&\n \ttest_expect_code 129 nongit git for-each-ref -h >usage &&\n \ttest_grep \"[Uu]sage: git for-each-ref \" usage\n '\n \n-test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n-\tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n-'\n-\n-test_expect_success 'Check valid format specifiers for date fields' '\n-\tgit for-each-ref --format=\"%(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:relative)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:short)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:local)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:iso8601)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:rfc2822)\" refs/heads\n-'\n-\n-test_expect_success 'Check invalid format specifiers are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n-'\n-\n-test_bad_atom () {\n-\tcase \"$1\" in\n-\thead) ref=refs/heads/main ;;\n-\t tag) ref=refs/tags/testtag ;;\n-\t sym) ref=refs/heads/sym ;;\n-\t   *) ref=$1 ;;\n-\tesac\n-\tformat=$2\n-\ttest_do=test_expect_${4:-success}\n-\n-\tprintf '%s\\n' \"$3\" >expect\n-\t$test_do $PREREQ \"err basic atom: $ref $format\" '\n-\t\ttest_must_fail git for-each-ref \\\n-\t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n-\t\ttest_cmp expect error\n-\t'\n-}\n-\n-test_bad_atom head 'authoremail:foo' \\\n-\t'fatal: unrecognized %(authoremail) argument: foo'\n-\n-test_bad_atom head 'authoremail:mailmap,trim,bar' \\\n-\t'fatal: unrecognized %(authoremail) argument: bar'\n-\n-test_bad_atom head 'authoremail:trim,' \\\n-\t'fatal: unrecognized %(authoremail) argument: '\n-\n-test_bad_atom head 'authoremail:mailmaptrim' \\\n-\t'fatal: unrecognized %(authoremail) argument: trim'\n-\n-test_bad_atom head 'committeremail: ' \\\n-\t'fatal: unrecognized %(committeremail) argument:  '\n-\n-test_bad_atom head 'committeremail: trim,foo' \\\n-\t'fatal: unrecognized %(committeremail) argument:  trim,foo'\n-\n-test_bad_atom head 'committeremail:mailmap,localpart ' \\\n-\t'fatal: unrecognized %(committeremail) argument:  '\n-\n-test_bad_atom head 'committeremail:trim_localpart' \\\n-\t'fatal: unrecognized %(committeremail) argument: _localpart'\n-\n-test_bad_atom head 'committeremail:localpart,,,trim' \\\n-\t'fatal: unrecognized %(committeremail) argument: ,,trim'\n-\n-test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \\\n-\t'fatal: unrecognized %(taggeremail) argument:  foo '\n-\n-test_bad_atom tag 'taggeremail:trim,localpart,' \\\n-\t'fatal: unrecognized %(taggeremail) argument: '\n-\n-test_bad_atom tag 'taggeremail:mailmap;localpart trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument: ;localpart trim'\n-\n-test_bad_atom tag 'taggeremail:localpart trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument:  trim'\n-\n-test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'\n-\n-test_date () {\n-\tf=$1 &&\n-\tcommitter_date=$2 &&\n-\tauthor_date=$3 &&\n-\ttagger_date=$4 &&\n-\tcat >expected <<-EOF &&\n-\t'refs/heads/main' '$committer_date' '$author_date'\n-\t'refs/tags/testtag' '$tagger_date'\n-\tEOF\n-\t(\n-\t\tgit for-each-ref --shell \\\n-\t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n-\t\t\trefs/heads &&\n-\t\tgit for-each-ref --shell \\\n-\t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n-\t\t\trefs/tags\n-\t) >actual &&\n-\ttest_cmp expected actual\n-}\n-\n-test_expect_success 'Check unformatted date fields output' '\n-\ttest_date \"\" \\\n-\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n-'\n-\n-test_expect_success 'Check format \"default\" formatted date fields output' '\n-\ttest_date default \\\n-\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n-'\n-\n-test_expect_success 'Check format \"default-local\" date fields output' '\n-\ttest_date default-local \"Mon Jul 3 23:18:43 2006\" \"Mon Jul 3 23:18:44 2006\" \"Mon Jul 3 23:18:45 2006\"\n-'\n-\n-# Don't know how to do relative check because I can't know when this script\n-# is going to be run and can't fake the current time to git, and hence can't\n-# provide expected output.  Instead, I'll just make sure that \"relative\"\n-# doesn't exit in error\n-test_expect_success 'Check format \"relative\" date fields output' '\n-\tf=relative &&\n-\t(git for-each-ref --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n-\tgit for-each-ref --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n-'\n-\n-# We just check that this is the same as \"relative\" for now.\n-test_expect_success 'Check format \"relative-local\" date fields output' '\n-\ttest_date relative-local \\\n-\t\t\"$(git for-each-ref --format=\"%(committerdate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(authordate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(taggerdate:relative)\" refs/tags)\"\n-'\n-\n-test_expect_success 'Check format \"short\" date fields output' '\n-\ttest_date short 2006-07-04 2006-07-04 2006-07-04\n-'\n-\n-test_expect_success 'Check format \"short-local\" date fields output' '\n-\ttest_date short-local 2006-07-03 2006-07-03 2006-07-03\n-'\n-\n-test_expect_success 'Check format \"local\" date fields output' '\n-\ttest_date local \\\n-\t\t\"Mon Jul 3 23:18:43 2006\" \\\n-\t\t\"Mon Jul 3 23:18:44 2006\" \\\n-\t\t\"Mon Jul 3 23:18:45 2006\"\n-'\n-\n-test_expect_success 'Check format \"iso8601\" date fields output' '\n-\ttest_date iso8601 \\\n-\t\t\"2006-07-04 01:18:43 +0200\" \\\n-\t\t\"2006-07-04 01:18:44 +0200\" \\\n-\t\t\"2006-07-04 01:18:45 +0200\"\n-'\n-\n-test_expect_success 'Check format \"iso8601-local\" date fields output' '\n-\ttest_date iso8601-local \"2006-07-03 23:18:43 +0000\" \"2006-07-03 23:18:44 +0000\" \"2006-07-03 23:18:45 +0000\"\n-'\n-\n-test_expect_success 'Check format \"rfc2822\" date fields output' '\n-\ttest_date rfc2822 \\\n-\t\t\"Tue, 4 Jul 2006 01:18:43 +0200\" \\\n-\t\t\"Tue, 4 Jul 2006 01:18:44 +0200\" \\\n-\t\t\"Tue, 4 Jul 2006 01:18:45 +0200\"\n-'\n-\n-test_expect_success 'Check format \"rfc2822-local\" date fields output' '\n-\ttest_date rfc2822-local \"Mon, 3 Jul 2006 23:18:43 +0000\" \"Mon, 3 Jul 2006 23:18:44 +0000\" \"Mon, 3 Jul 2006 23:18:45 +0000\"\n-'\n-\n-test_expect_success 'Check format \"raw\" date fields output' '\n-\ttest_date raw \"1151968723 +0200\" \"1151968724 +0200\" \"1151968725 +0200\"\n-'\n-\n-test_expect_success 'Check format \"raw-local\" date fields output' '\n-\ttest_date raw-local \"1151968723 +0000\" \"1151968724 +0000\" \"1151968725 +0000\"\n-'\n-\n-test_expect_success 'Check format of strftime date fields' '\n-\techo \"my date is 2006-07-04\" >expected &&\n-\tgit for-each-ref \\\n-\t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n-\t  refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Check format of strftime-local date fields' '\n-\techo \"my date is 2006-07-03\" >expected &&\n-\tgit for-each-ref \\\n-\t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n-\t  refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'exercise strftime with odd fields' '\n-\techo >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:)\" refs/heads >actual &&\n-\ttest_cmp expected actual &&\n-\tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n-\techo $long >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/heads/main\n-refs/remotes/origin/main\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'Verify ascending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=refname >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/remotes/origin/main\n-refs/heads/main\n-EOF\n-\n-test_expect_success 'Verify descending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=-refname >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Give help even with invalid sort atoms' '\n-\ttest_expect_code 129 git for-each-ref --sort=bogus -h >actual 2>&1 &&\n-\tgrep \"^usage: git for-each-ref\" actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/tags/testtag-2\n-EOF\n-\n-test_expect_success 'exercise patterns with prefixes' '\n-\tgit tag testtag-2 &&\n-\ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/tags/testtag-2\n-EOF\n-\n-test_expect_success 'exercise glob patterns with prefixes' '\n-\tgit tag testtag-2 &&\n-\ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/bar\n-refs/tags/baz\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'exercise patterns with prefix exclusions' '\n-\tfor tag in foo/one foo/two foo/three bar baz\n-\tdo\n-\t\tgit tag \"$tag\" || return 1\n-\tdone &&\n-\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/bar\n-refs/tags/baz\n-refs/tags/foo/one\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'exercise patterns with pattern exclusions' '\n-\tfor tag in foo/one foo/two foo/three bar baz\n-\tdo\n-\t\tgit tag \"$tag\" || return 1\n-\tdone &&\n-\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-'refs/heads/main'\n-'refs/remotes/origin/main'\n-'refs/tags/testtag'\n-EOF\n-\n-test_expect_success 'Quoting style: shell' '\n-\tgit for-each-ref --shell --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Quoting style: perl' '\n-\tgit for-each-ref --perl --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Quoting style: python' '\n-\tgit for-each-ref --python --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-\"refs/heads/main\"\n-\"refs/remotes/origin/main\"\n-\"refs/tags/testtag\"\n-EOF\n-\n-test_expect_success 'Quoting style: tcl' '\n-\tgit for-each-ref --tcl --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n-\ttest_expect_success \"more than one quoting style: $i\" \"\n-\t\ttest_must_fail git for-each-ref $i 2>err &&\n-\t\tgrep '^error: more than one quoting style' err\n-\t\"\n-done\n-\n-test_expect_success 'setup for upstream:track[short]' '\n-\ttest_commit two\n-'\n-\n-test_atom head upstream:track '[ahead 1]'\n-test_atom head upstream:trackshort '>'\n-test_atom head upstream:track,nobracket 'ahead 1'\n-test_atom head upstream:nobracket,track 'ahead 1'\n-\n-test_expect_success 'setup for push:track[short]' '\n-\ttest_commit third &&\n-\tgit update-ref refs/remotes/myfork/main main &&\n-\tgit reset main~1\n-'\n-\n-test_atom head push:track '[behind 1]'\n-test_atom head push:trackshort '<'\n-\n-test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:track)\" 2>/dev/null &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:trackshort)\" 2>/dev/null\n-'\n-\n-test_expect_success 'Check that :track[short] works when upstream is invalid' '\n-\tcat >expected <<-\\EOF &&\n-\t[gone]\n-\n-\tEOF\n-\ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n-\tgit config branch.main.merge refs/heads/does-not-exist &&\n-\tgit for-each-ref \\\n-\t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n-\t\trefs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Check for invalid refname format' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n-'\n-\n-test_expect_success 'set up color tests' '\n-\tcat >expected.color <<-EOF &&\n-\t$(git rev-parse --short refs/heads/main) <GREEN>main<RESET>\n-\t$(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>\n-\t$(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>\n-\t$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>\n-\t$(git rev-parse --short refs/tags/third) <GREEN>third<RESET>\n-\t$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>\n-\tEOF\n-\tsed \"s/<[^>]*>//g\" <expected.color >expected.bare &&\n-\tcolor_format=\"%(objectname:short) %(color:green)%(refname:short)\"\n-'\n-\n-test_expect_success TTY '%(color) shows color with a tty' '\n-\ttest_terminal git for-each-ref --format=\"$color_format\" >actual.raw &&\n-\ttest_decode_color <actual.raw >actual &&\n-\ttest_cmp expected.color actual\n-'\n-\n-test_expect_success '%(color) does not show color without tty' '\n-\tTERM=vt100 git for-each-ref --format=\"$color_format\" >actual &&\n-\ttest_cmp expected.bare actual\n-'\n-\n-test_expect_success '--color can override tty check' '\n-\tgit for-each-ref --color --format=\"$color_format\" >actual.raw &&\n-\ttest_decode_color <actual.raw >actual &&\n-\ttest_cmp expected.color actual\n-'\n-\n-test_expect_success 'color.ui=always does not override tty check' '\n-\tgit -c color.ui=always for-each-ref --format=\"$color_format\" >actual &&\n-\ttest_cmp expected.bare actual\n-'\n-\n-test_expect_success 'setup for describe atom tests' '\n-\tgit init -b master describe-repo &&\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\ttest_commit --no-tag one &&\n-\t\tgit tag tagone &&\n-\n-\t\ttest_commit --no-tag two &&\n-\t\tgit tag -a -m \"tag two\" tagtwo\n-\t)\n-'\n-\n-test_expect_success 'describe atom vs git describe' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\tgit for-each-ref --format=\"%(objectname)\" \\\n-\t\t\trefs/tags/ >obj &&\n-\t\twhile read hash\n-\t\tdo\n-\t\t\tif desc=$(git describe $hash)\n-\t\t\tthen\n-\t\t\t\t: >expect-contains-good\n-\t\t\telse\n-\t\t\t\t: >expect-contains-bad\n-\t\t\tfi &&\n-\t\t\techo \"$hash $desc\" || return 1\n-\t\tdone <obj >expect &&\n-\t\ttest_path_exists expect-contains-good &&\n-\t\ttest_path_exists expect-contains-bad &&\n-\n-\t\tgit for-each-ref --format=\"%(objectname) %(describe)\" \\\n-\t\t\trefs/tags/ >actual 2>err &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest_must_be_empty err\n-\t)\n-'\n-\n-test_expect_success 'describe:tags vs describe --tags' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit describe --tags >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:tags)\" \\\n-\t\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\t# Case 1: We have commits between HEAD and the most\n-\t\t#\t  recent tag reachable from it\n-\t\ttest_commit --no-tag file &&\n-\t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual &&\n-\n-\t\t# Make sure the hash used is at least 14 digits long\n-\t\tsed -e \"s/^.*-g\\([0-9a-f]*\\)$/\\1/\" <actual >hexpart &&\n-\t\ttest 15 -le $(wc -c <hexpart) &&\n-\n-\t\t# Case 2: We have a tag at HEAD, describe directly gives\n-\t\t#\t  the name of the tag\n-\t\tgit tag -a -m tagged tagname &&\n-\t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest tagname = $(cat actual)\n-\t)\n-'\n-\n-test_expect_success 'describe:match=... vs describe --match ...' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit tag -a -m \"tag foo\" tag-foo &&\n-\t\tgit describe --match \"*-foo\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:match=\"*-foo\")\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit tag -a -m \"tag bar\" tag-bar &&\n-\t\tgit describe --exclude \"*-bar\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:exclude=\"*-bar\")\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'deref with describe atom' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tcat >expect <<-\\EOF &&\n-\n-\t\ttagname\n-\t\ttagname\n-\t\ttagname\n-\n-\t\ttagtwo\n-\t\tEOF\n-\t\tgit for-each-ref --format=\"%(*describe)\" >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'err on bad describe atom arg' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\t# The bad arg is the only arg passed to describe atom\n-\t\tcat >expect <<-\\EOF &&\n-\t\tfatal: unrecognized %(describe) argument: baz\n-\t\tEOF\n-\t\ttest_must_fail git for-each-ref --format=\"%(describe:baz)\" \\\n-\t\t\trefs/heads/master 2>actual &&\n-\t\ttest_cmp expect actual &&\n-\n-\t\t# The bad arg is in the middle of the option string\n-\t\t# passed to the describe atom\n-\t\tcat >expect <<-\\EOF &&\n-\t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n-\t\tEOF\n-\t\ttest_must_fail git for-each-ref \\\n-\t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n-\t\t\tref/heads/master 2>actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-cat >expected <<\\EOF\n-heads/main\n-tags/main\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs (strict)' '\n-\tgit config --bool core.warnambiguousrefs true &&\n-\tgit checkout -b newtag &&\n-\techo \"Using $datestamp\" > one &&\n-\tgit add one &&\n-\tgit commit -m \"Branch\" &&\n-\tsetdate_and_increment &&\n-\tgit tag -m \"Tagging at $datestamp\" main &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-heads/main\n-main\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs (loose)' '\n-\tgit config --bool core.warnambiguousrefs false &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-heads/ambiguous\n-ambiguous\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n-\tgit checkout main &&\n-\tgit tag ambiguous testtag^0 &&\n-\tgit branch ambiguous testtag^0 &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'create tag without tagger' '\n-\tgit tag -a -m \"Broken tag\" taggerless &&\n-\tgit tag -f taggerless $(git cat-file tag taggerless |\n-\t\tsed -e \"/^tagger /d\" |\n-\t\tgit hash-object --literally --stdin -w -t tag)\n-'\n-\n-test_atom refs/tags/taggerless type 'commit'\n-test_atom refs/tags/taggerless tag 'taggerless'\n-test_atom refs/tags/taggerless tagger ''\n-test_atom refs/tags/taggerless taggername ''\n-test_atom refs/tags/taggerless taggeremail ''\n-test_atom refs/tags/taggerless taggeremail:trim ''\n-test_atom refs/tags/taggerless taggeremail:localpart ''\n-test_atom refs/tags/taggerless taggerdate ''\n-test_atom refs/tags/taggerless committer ''\n-test_atom refs/tags/taggerless committername ''\n-test_atom refs/tags/taggerless committeremail ''\n-test_atom refs/tags/taggerless committeremail:trim ''\n-test_atom refs/tags/taggerless committeremail:localpart ''\n-test_atom refs/tags/taggerless committerdate ''\n-test_atom refs/tags/taggerless subject 'Broken tag'\n-\n-test_expect_success 'an unusual tag with an incomplete line' '\n-\n-\tgit tag -m \"bogo\" bogo &&\n-\tbogo=$(git cat-file tag bogo) &&\n-\tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n-\tgit tag -f bogo \"$bogo\" &&\n-\tgit for-each-ref --format \"%(body)\" refs/tags/bogo\n-\n-'\n-\n-test_expect_success 'create tag with subject and body content' '\n-\tcat >>msg <<-\\EOF &&\n-\t\tthe subject line\n-\n-\t\tfirst body line\n-\t\tsecond body line\n-\tEOF\n-\tgit tag -F msg subject-body\n-'\n-test_atom refs/tags/subject-body subject 'the subject line'\n-test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n-test_atom refs/tags/subject-body body 'first body line\n-second body line\n-'\n-test_atom refs/tags/subject-body contents 'the subject line\n-\n-first body line\n-second body line\n-'\n-\n-test_expect_success 'create tag with multiline subject' '\n-\tcat >msg <<-\\EOF &&\n-\t\tfirst subject line\n-\t\tsecond subject line\n-\n-\t\tfirst body line\n-\t\tsecond body line\n-\tEOF\n-\tgit tag -F msg multiline\n-'\n-test_atom refs/tags/multiline subject 'first subject line second subject line'\n-test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n-test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n-test_atom refs/tags/multiline body 'first body line\n-second body line\n-'\n-test_atom refs/tags/multiline contents:body 'first body line\n-second body line\n-'\n-test_atom refs/tags/multiline contents:signature ''\n-test_atom refs/tags/multiline contents 'first subject line\n-second subject line\n-\n-first body line\n-second body line\n-'\n-\n-test_expect_success GPG 'create signed tags' '\n-\tgit tag -s -m \"\" signed-empty &&\n-\tgit tag -s -m \"subject line\" signed-short &&\n-\tcat >msg <<-\\EOF &&\n-\tsubject line\n-\n-\tbody contents\n-\tEOF\n-\tgit tag -s -F msg signed-long\n-'\n-\n-sig='-----BEGIN PGP SIGNATURE-----\n------END PGP SIGNATURE-----\n-'\n-\n-PREREQ=GPG\n-test_atom refs/tags/signed-empty subject ''\n-test_atom refs/tags/signed-empty subject:sanitize ''\n-test_atom refs/tags/signed-empty contents:subject ''\n-test_atom refs/tags/signed-empty body \"$sig\"\n-test_atom refs/tags/signed-empty contents:body ''\n-test_atom refs/tags/signed-empty contents:signature \"$sig\"\n-test_atom refs/tags/signed-empty contents \"$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n-\tgit cat-file tag refs/tags/signed-empty >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_atom refs/tags/signed-short subject 'subject line'\n-test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n-test_atom refs/tags/signed-short contents:subject 'subject line'\n-test_atom refs/tags/signed-short body \"$sig\"\n-test_atom refs/tags/signed-short contents:body ''\n-test_atom refs/tags/signed-short contents:signature \"$sig\"\n-test_atom refs/tags/signed-short contents \"subject line\n-$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n-\tgit cat-file tag refs/tags/signed-short >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-short >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_atom refs/tags/signed-long subject 'subject line'\n-test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n-test_atom refs/tags/signed-long contents:subject 'subject line'\n-test_atom refs/tags/signed-long body \"body contents\n-$sig\"\n-test_atom refs/tags/signed-long contents:body 'body contents\n-'\n-test_atom refs/tags/signed-long contents:signature \"$sig\"\n-test_atom refs/tags/signed-long contents \"subject line\n-\n-body contents\n-$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n-\tgit cat-file tag refs/tags/signed-long >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-long >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_expect_success 'set up refs pointing to tree and blob' '\n-\tgit update-ref refs/mytrees/first refs/heads/main^{tree} &&\n-\tgit update-ref refs/myblobs/first refs/heads/main:one\n-'\n-\n-test_atom refs/mytrees/first subject \"\"\n-test_atom refs/mytrees/first contents:subject \"\"\n-test_atom refs/mytrees/first body \"\"\n-test_atom refs/mytrees/first contents:body \"\"\n-test_atom refs/mytrees/first contents:signature \"\"\n-test_atom refs/mytrees/first contents \"\"\n-\n-test_expect_success 'basic atom: refs/mytrees/first raw' '\n-\tgit cat-file tree refs/mytrees/first >expected &&\n-\techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual &&\n-\tgit cat-file -s refs/mytrees/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_atom refs/myblobs/first subject \"\"\n-test_atom refs/myblobs/first contents:subject \"\"\n-test_atom refs/myblobs/first body \"\"\n-test_atom refs/myblobs/first contents:body \"\"\n-test_atom refs/myblobs/first contents:signature \"\"\n-test_atom refs/myblobs/first contents \"\"\n-\n-test_expect_success 'basic atom: refs/myblobs/first raw' '\n-\tgit cat-file blob refs/myblobs/first >expected &&\n-\techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/myblobs/first >actual &&\n-\ttest_cmp expected actual &&\n-\tgit cat-file -s refs/myblobs/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'set up refs pointing to binary blob' '\n-\tprintf \"a\\0b\\0c\" >blob1 &&\n-\tprintf \"a\\0c\\0b\" >blob2 &&\n-\tprintf \"\\0a\\0b\\0c\" >blob3 &&\n-\tprintf \"abc\" >blob4 &&\n-\tprintf \"\\0 \\0 \\0 \" >blob5 &&\n-\tprintf \"\\0 \\0a\\0 \" >blob6 &&\n-\tprintf \"  \" >blob7 &&\n-\t>blob8 &&\n-\tobj=$(git hash-object -w blob1) &&\n-\tgit update-ref refs/myblobs/blob1 \"$obj\" &&\n-\tobj=$(git hash-object -w blob2) &&\n-\tgit update-ref refs/myblobs/blob2 \"$obj\" &&\n-\tobj=$(git hash-object -w blob3) &&\n-\tgit update-ref refs/myblobs/blob3 \"$obj\" &&\n-\tobj=$(git hash-object -w blob4) &&\n-\tgit update-ref refs/myblobs/blob4 \"$obj\" &&\n-\tobj=$(git hash-object -w blob5) &&\n-\tgit update-ref refs/myblobs/blob5 \"$obj\" &&\n-\tobj=$(git hash-object -w blob6) &&\n-\tgit update-ref refs/myblobs/blob6 \"$obj\" &&\n-\tobj=$(git hash-object -w blob7) &&\n-\tgit update-ref refs/myblobs/blob7 \"$obj\" &&\n-\tobj=$(git hash-object -w blob8) &&\n-\tgit update-ref refs/myblobs/blob8 \"$obj\"\n-'\n-\n-test_expect_success 'Verify sorts with raw' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob8\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/blob3\n-\trefs/myblobs/blob7\n-\trefs/mytrees/first\n-\trefs/myblobs/first\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob4\n-\trefs/heads/main\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw \\\n-\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Verify sorts with raw:size' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob8\n-\trefs/myblobs/blob7\n-\trefs/myblobs/blob4\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob3\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/first\n-\trefs/mytrees/first\n-\trefs/heads/main\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw:size \\\n-\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'validate raw atom with %(if:equals)' '\n-\tcat >expected <<-EOF &&\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\trefs/myblobs/blob4\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tEOF\n-\tgit for-each-ref --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n-\t\trefs/myblobs/ refs/heads/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'validate raw atom with %(if:notequals)' '\n-\tcat >expected <<-EOF &&\n-\trefs/heads/ambiguous\n-\trefs/heads/main\n-\trefs/heads/newtag\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob3\n-\tequals\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/blob7\n-\trefs/myblobs/blob8\n-\trefs/myblobs/first\n-\tEOF\n-\tgit for-each-ref --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n-\t\trefs/myblobs/ refs/heads/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'empty raw refs with %(if)' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob1 not empty\n-\trefs/myblobs/blob2 not empty\n-\trefs/myblobs/blob3 not empty\n-\trefs/myblobs/blob4 not empty\n-\trefs/myblobs/blob5 not empty\n-\trefs/myblobs/blob6 not empty\n-\trefs/myblobs/blob7 empty\n-\trefs/myblobs/blob8 empty\n-\trefs/myblobs/first not empty\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n-\t\trefs/myblobs/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '%(raw) with --python must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --python\n-'\n-\n-test_expect_success '%(raw) with --tcl must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --tcl\n-'\n-\n-test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n-\tcmp blob1 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n-\tcmp blob3 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n-\tcmp blob8 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n-\tcmp one actual &&\n-\tgit cat-file tree refs/mytrees/first > expected &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n-\tcmp expected actual\n-'\n-\n-test_expect_success '%(raw) with --shell must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --shell\n-'\n-\n-test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --sort=raw --shell\n-'\n-\n-test_expect_success '%(raw:size) with --shell' '\n-\tgit for-each-ref --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n-\tgit for-each-ref --format=\"%(raw:size)\" --shell >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --format compare with cat-file --batch' '\n-\tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n-\tgit for-each-ref --format=\"%(objectname) %(objecttype) %(objectsize)\n-%(raw)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'verify sorts with contents:size' '\n-\tcat >expect <<-\\EOF &&\n-\trefs/heads/main\n-\trefs/heads/newtag\n-\trefs/heads/ambiguous\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\t--sort=contents:size refs/heads/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'set up multiple-sort tags' '\n-\tfor when in 100000 200000\n-\tdo\n-\t\tfor email in user1 user2\n-\t\tdo\n-\t\t\tfor ref in ref1 ref2\n-\t\t\tdo\n-\t\t\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n-\t\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n-\t\t\t\tgit tag -m \"tag $ref-$when-$email\" \\\n-\t\t\t\tmulti-$ref-$when-$email || return 1\n-\t\t\tdone\n-\t\tdone\n-\tdone\n-'\n-\n-test_expect_success 'Verify sort with multiple keys' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=-refname \\\n-\t\t--sort=taggeremail \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'equivalent sorts fall back on refname' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '--no-sort cancels the previous sort keys' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=-refname \\\n-\t\t--sort=taggeremail \\\n-\t\t--no-sort \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n-\tcat >expected <<-\\EOF &&\n-\trefs/tags/multi-ref1-100000-user1\n-\trefs/tags/multi-ref1-100000-user2\n-\trefs/tags/multi-ref1-200000-user1\n-\trefs/tags/multi-ref1-200000-user2\n-\trefs/tags/multi-ref2-100000-user1\n-\trefs/tags/multi-ref2-100000-user2\n-\trefs/tags/multi-ref2-200000-user1\n-\trefs/tags/multi-ref2-200000-user2\n-\tEOF\n-\n-\t# Sort the results with `sort` for a consistent comparison against\n-\t# expected\n-\tgit for-each-ref \\\n-\t\t--format=\"%(refname)\" \\\n-\t\t--no-sort \\\n-\t\t\"refs/tags/multi-*\" | sort >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'set up custom date sorting' '\n-\t# Dates:\n-\t# - Wed Feb 07 2024 21:34:20 +0000\n-\t# - Tue Dec 14 1999 00:05:22 +0000\n-\t# - Fri Jun 04 2021 11:26:51 +0000\n-\t# - Mon Jan 22 2007 16:44:01 GMT+0000\n-\ti=1 &&\n-\tfor when in 1707341660 945129922 1622806011 1169484241\n-\tdo\n-\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n-\t\tGIT_COMMITTER_EMAIL=\"user@example.com\" \\\n-\t\tgit tag -m \"tag $when\" custom-dates-$i &&\n-\t\ti=$(($i+1)) || return 1\n-\tdone\n-'\n-\n-test_expect_success 'sort by date defaults to full timestamp' '\n-\tcat >expected <<-\\EOF &&\n-\t945129922 refs/tags/custom-dates-2\n-\t1169484241 refs/tags/custom-dates-4\n-\t1622806011 refs/tags/custom-dates-3\n-\t1707341660 refs/tags/custom-dates-1\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(creatordate:unix) %(refname)\" \\\n-\t\t--sort=creatordate \\\n-\t\t\"refs/tags/custom-dates-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'sort by custom date format' '\n-\tcat >expected <<-\\EOF &&\n-\t00:05:22 refs/tags/custom-dates-2\n-\t11:26:51 refs/tags/custom-dates-3\n-\t16:44:01 refs/tags/custom-dates-4\n-\t21:34:20 refs/tags/custom-dates-1\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n-\t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n-\t\t\"refs/tags/custom-dates-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n-\ttest_when_finished \"git checkout main\" &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n-\tsed -e \"s/^\\* /  /\" actual >expect &&\n-\tgit checkout --orphan orphaned-branch &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-cat >trailers <<EOF\n-Reviewed-by: A U Thor <author@example.com>\n-Signed-off-by: A U Thor <author@example.com>\n-[ v2 updated patch description ]\n-Acked-by: A U Thor\n-  <author@example.com>\n-EOF\n-\n-unfold () {\n-\tperl -0pe 's/\\n\\s+/ /g'\n-}\n-\n-test_expect_success 'set up trailers for next test' '\n-\techo \"Some contents\" > two &&\n-\tgit add two &&\n-\tgit commit -F - <<-EOF\n-\ttrailers: this commit message has trailers\n-\n-\tSome message contents\n-\n-\t$(cat trailers)\n-\tEOF\n-'\n-\n-test_trailer_option () {\n-\tif test \"$#\" -eq 3\n-\tthen\n-\t\tprereq=\"$1\"\n-\t\tshift\n-\tfi &&\n-\ttitle=$1 option=$2\n-\tcat >expect\n-\ttest_expect_success $prereq \"$title\" '\n-\t\tgit for-each-ref --format=\"%($option)\" refs/heads/main >actual &&\n-\t\ttest_cmp expect actual &&\n-\t\tgit for-each-ref --format=\"%(contents:$option)\" refs/heads/main >actual &&\n-\t\ttest_cmp expect actual\n-\t'\n-}\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \\\n-\t'trailers:unfold' <<-EOF\n-\t$(unfold <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only) shows only \"key: value\" trailers' \\\n-\t'trailers:only' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=no,only=true) shows only \"key: value\" trailers' \\\n-\t'trailers:only=no,only=true' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=yes) shows only \"key: value\" trailers' \\\n-\t'trailers:only=yes' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=no) shows all trailers' \\\n-\t'trailers:only=no' <<-EOF\n-\t$(cat trailers)\n-\n-\tEOF\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \\\n-\t'trailers:only,unfold' <<-EOF\n-\t$(grep -v patch.description <trailers | unfold)\n-\n-\tEOF\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \\\n-\t'trailers:unfold,only' <<-EOF\n-\t$(grep -v patch.description <trailers | unfold)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) shows that trailer' \\\n-\t'trailers:key=Signed-off-by' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) is case insensitive' \\\n-\t'trailers:key=SiGned-oFf-bY' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo:) trailing colon also works' \\\n-\t'trailers:key=Signed-off-by:' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) multiple keys' \\\n-\t'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF\n-\tReviewed-by: A U Thor <author@example.com>\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=nonexistent) becomes empty' \\\n-\t'trailers:key=Shined-off-by:' <<-EOF\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \\\n-\t'trailers:key=Acked-by' <<-EOF\n-\t$(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \\\n-\t'trailers:key=Signed-Off-by,unfold' <<-EOF\n-\t$(unfold <trailers | grep Signed-off-by)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \\\n-\t'trailers:key=Signed-off-by,only=no' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\t$(grep patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \\\n-\t'trailers:key=Signed-off-by,valueonly' <<-EOF\n-\tA U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:separator) changes separator' \\\n-\t'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>\n-\tEOF\n-\n-test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \\\n-\t'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by,A U Thor <author@example.com>\n-\tSigned-off-by,A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \\\n-\t'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>\n-\tEOF\n-\n-test_expect_success 'multiple %(trailers) use their own options' '\n-\tgit tag -F - tag-with-trailers <<-\\EOF &&\n-\tbody\n-\n-\tone: foo\n-\tone: bar\n-\ttwo: baz\n-\ttwo: qux\n-\tEOF\n-\tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n-\tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n-\tgit for-each-ref --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n-\tcat >expect <<-\\EOF &&\n-\toneWfooXoneWbar\n-\ttwoYbazZtwoYqux\n-\tEOF\n-\ttest_cmp expect actual\n-'\n-\n-test_failing_trailer_option () {\n-\ttitle=$1 option=$2\n-\tcat >expect\n-\ttest_expect_success \"$title\" '\n-\t\t# error message cannot be checked under i18n\n-\t\ttest_must_fail git for-each-ref --format=\"%($option)\" refs/heads/main 2>actual &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest_must_fail git for-each-ref --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n-\t\ttest_cmp expect actual\n-\t'\n-}\n-\n-test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \\\n-\t'trailers:unsupported' <<-\\EOF\n-\tfatal: unknown %(trailers) argument: unsupported\n-\tEOF\n-\n-test_failing_trailer_option '%(trailers:key) without value is error' \\\n-\t'trailers:key' <<-\\EOF\n-\tfatal: expected %(trailers:key=<value>)\n-\tEOF\n-\n-test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '\n-\tcat >expect <<-EOF &&\n-\tfatal: unrecognized %(contents) argument: trailersonly\n-\tEOF\n-\ttest_must_fail git for-each-ref --format=\"%(contents:trailersonly)\" 2>actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'basic atom: head contents:trailers' '\n-\tgit for-each-ref --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\t# git for-each-ref ends with a blank line\n-\tcat >expect <<-EOF &&\n-\t$(cat trailers)\n-\n-\tEOF\n-\ttest_cmp expect actual.clean\n-'\n-\n-test_expect_success 'basic atom: rest must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(rest)\" refs/heads/main\n-'\n-\n-test_expect_success 'HEAD atom does not take arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(HEAD:foo)\" 2>err &&\n-\techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'subject atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(subject:foo)\" 2>err &&\n-\techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'refname atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:foo)\" 2>err &&\n-\techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'trailer parsing not fooled by --- line' '\n-\tgit commit --allow-empty -F - <<-\\EOF &&\n-\tthis is the subject\n-\n-\tThis is the body. The message has a \"---\" line which would confuse a\n-\tmessage+patch parser. But here we know we have only a commit message,\n-\tso we get it right.\n-\n-\ttrailer: wrong\n-\t---\n-\tThis is more body.\n-\n-\ttrailer: right\n-\tEOF\n-\n-\t{\n-\t\techo \"trailer: right\" &&\n-\t\techo\n-\t} >expect &&\n-\tgit for-each-ref --format=\"%(trailers)\" refs/heads/main >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'Add symbolic ref for the following tests' '\n-\tgit symbolic-ref refs/heads/sym refs/heads/main\n-'\n-\n-cat >expected <<EOF\n-refs/heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref) atom' '\n-\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:short) atom' '\n-\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-main\n-heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual &&\n-\n-\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-refs\n-refs/heads\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success ':remotename and :remoteref' '\n-\tgit init remote-tests &&\n-\t(\n-\t\tcd remote-tests &&\n-\t\ttest_commit initial &&\n-\t\tgit branch -M main &&\n-\t\tgit remote add from fifth.coffee:blub &&\n-\t\tgit config branch.main.remote from &&\n-\t\tgit config branch.main.merge refs/heads/stable &&\n-\t\tgit remote add to southridge.audio:repo &&\n-\t\tgit config remote.to.push \"refs/heads/*:refs/heads/pushed/*\" &&\n-\t\tgit config branch.main.pushRemote to &&\n-\t\tfor pair in \"%(upstream)=refs/remotes/from/stable\" \\\n-\t\t\t\"%(upstream:remotename)=from\" \\\n-\t\t\t\"%(upstream:remoteref)=refs/heads/stable\" \\\n-\t\t\t\"%(push)=refs/remotes/to/pushed/main\" \\\n-\t\t\t\"%(push:remotename)=to\" \\\n-\t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n-\t\tdo\n-\t\t\techo \"${pair#*=}\" >expect &&\n-\t\t\tgit for-each-ref --format=\"${pair%=*}\" \\\n-\t\t\t\trefs/heads/main >actual &&\n-\t\t\ttest_cmp expect actual || exit 1\n-\t\tdone &&\n-\t\tgit branch push-simple &&\n-\t\tgit config branch.push-simple.pushRemote from &&\n-\t\tactual=\"$(git for-each-ref \\\n-\t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n-\t\t\trefs/heads/push-simple)\" &&\n-\t\ttest from, = \"$actual\"\n-\t)\n-'\n-\n-test_expect_success 'for-each-ref --ignore-case ignores case' '\n-\tgit for-each-ref --format=\"%(refname)\" refs/heads/MAIN >actual &&\n-\ttest_must_be_empty actual &&\n-\n-\techo refs/heads/main >expect &&\n-\tgit for-each-ref --format=\"%(refname)\" --ignore-case \\\n-\t\trefs/heads/MAIN >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --omit-empty works' '\n-\tgit for-each-ref --format=\"%(refname)\" >actual &&\n-\ttest_line_count -gt 1 actual &&\n-\tgit for-each-ref --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n-\techo refs/heads/main >expect &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n-\t# name refs numerically to avoid case-insensitive filesystem conflicts\n-\tnr=0 &&\n-\tfor email in a A b B\n-\tdo\n-\t\tfor subject in a A b B\n-\t\tdo\n-\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n-\t\t\tgit tag -m \"tag $subject\" icase-$(printf %02d $nr) &&\n-\t\t\tnr=$((nr+1))||\n-\t\t\treturn 1\n-\t\tdone\n-\tdone &&\n-\tgit for-each-ref --ignore-case \\\n-\t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n-\t\t--sort=refname \\\n-\t\t--sort=subject \\\n-\t\t--sort=taggeremail \\\n-\t\trefs/tags/icase-* >actual &&\n-\tcat >expect <<-\\EOF &&\n-\t<a@example.com> tag a refs/tags/icase-00\n-\t<a@example.com> tag A refs/tags/icase-01\n-\t<A@example.com> tag a refs/tags/icase-04\n-\t<A@example.com> tag A refs/tags/icase-05\n-\t<a@example.com> tag b refs/tags/icase-02\n-\t<a@example.com> tag B refs/tags/icase-03\n-\t<A@example.com> tag b refs/tags/icase-06\n-\t<A@example.com> tag B refs/tags/icase-07\n-\t<b@example.com> tag a refs/tags/icase-08\n-\t<b@example.com> tag A refs/tags/icase-09\n-\t<B@example.com> tag a refs/tags/icase-12\n-\t<B@example.com> tag A refs/tags/icase-13\n-\t<b@example.com> tag b refs/tags/icase-10\n-\t<b@example.com> tag B refs/tags/icase-11\n-\t<B@example.com> tag b refs/tags/icase-14\n-\t<B@example.com> tag B refs/tags/icase-15\n-\tEOF\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref reports broken tags' '\n-\tgit tag -m \"good tag\" broken-tag-good HEAD &&\n-\tgit cat-file tag broken-tag-good >good &&\n-\tsed s/commit/blob/ <good >bad &&\n-\tbad=$(git hash-object -w -t tag bad) &&\n-\tgit update-ref refs/tags/broken-tag-bad $bad &&\n-\ttest_must_fail git for-each-ref --format=\"%(*objectname)\" \\\n-\t\trefs/tags/broken-tag-*\n-'\n-\n-test_expect_success 'set up tag with signature and no blank lines' '\n-\tgit tag -F - fake-sig-no-blanks <<-\\EOF\n-\tthis is the subject\n-\t-----BEGIN PGP SIGNATURE-----\n-\tnot a real signature, but we just care about the\n-\tsubject/body parsing. It is important here that\n-\tthere are no blank lines in the signature.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-'\n-\n-test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'\n-test_atom refs/tags/fake-sig-no-blanks contents:body ''\n-test_atom refs/tags/fake-sig-no-blanks contents:signature \"$sig\"\n-\n-test_expect_success 'set up tag with CRLF signature' '\n-\tappend_cr <<-\\EOF |\n-\tthis is the subject\n-\t-----BEGIN PGP SIGNATURE-----\n-\n-\tnot a real signature, but we just care about\n-\tthe subject/body parsing. It is important here\n-\tthat there is a blank line separating this\n-\tfrom the signature header.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-\tgit tag -F - --cleanup=verbatim fake-sig-crlf\n-'\n-\n-test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'\n-test_atom refs/tags/fake-sig-crlf contents:body ''\n-\n-# CRLF is retained in the signature, so we have to pass our expected value\n-# through append_cr. But test_atom requires a shell string, which means command\n-# substitution, and the shell will strip trailing newlines from the output of\n-# the substitution. Hack around it by adding and then removing a dummy line.\n-sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n-sig_crlf=${sig_crlf%dummy}\n-test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n-\n-test_expect_success 'set up tag with signature and trailers' '\n-\tgit tag -F - fake-sig-trailer <<-\\EOF\n-\tthis is the subject\n-\n-\tthis is the body\n-\n-\tMy-Trailer: foo\n-\t-----BEGIN PGP SIGNATURE-----\n-\n-\tnot a real signature, but we just care about the\n-\tsubject/body/trailer parsing.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-'\n-\n-# use \"separator=\" here to suppress the terminating newline\n-test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n-\n-test_expect_success 'git for-each-ref --stdin: empty' '\n-\t>in &&\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\tgit for-each-ref --format=\"%(refname)\" >expect &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'git for-each-ref --stdin: fails if extra args' '\n-\t>in &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname)\" \\\n-\t\t--stdin refs/heads/extra <in 2>err &&\n-\tgrep \"unknown arguments supplied with --stdin\" err\n-'\n-\n-test_expect_success 'git for-each-ref --stdin: matches' '\n-\tcat >in <<-EOF &&\n-\trefs/tags/multi*\n-\trefs/heads/amb*\n-\tEOF\n-\n-\tcat >expect <<-EOF &&\n-\trefs/heads/ambiguous\n-\trefs/tags/multi-ref1-100000-user1\n-\trefs/tags/multi-ref1-100000-user2\n-\trefs/tags/multi-ref1-200000-user1\n-\trefs/tags/multi-ref1-200000-user2\n-\trefs/tags/multi-ref2-100000-user1\n-\trefs/tags/multi-ref2-100000-user2\n-\trefs/tags/multi-ref2-200000-user1\n-\trefs/tags/multi-ref2-200000-user2\n-\trefs/tags/multiline\n-\tEOF\n-\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'git for-each-ref with non-existing refs' '\n-\tcat >in <<-EOF &&\n-\trefs/heads/this-ref-does-not-exist\n-\trefs/tags/bogus\n-\tEOF\n-\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\ttest_must_be_empty actual &&\n-\n-\txargs git for-each-ref --format=\"%(refname)\" <in >actual &&\n-\ttest_must_be_empty actual\n-'\n-\n-test_expect_success 'git for-each-ref with nested tags' '\n-\tgit tag -am \"Normal tag\" nested/base HEAD &&\n-\tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n-\tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n-\n-\thead_oid=\"$(git rev-parse HEAD)\" &&\n-\tbase_tag_oid=\"$(git rev-parse refs/tags/nested/base)\" &&\n-\tnest1_tag_oid=\"$(git rev-parse refs/tags/nested/nest1)\" &&\n-\tnest2_tag_oid=\"$(git rev-parse refs/tags/nested/nest2)\" &&\n-\n-\tcat >expect <<-EOF &&\n-\trefs/tags/nested/base $base_tag_oid tag $head_oid commit\n-\trefs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit\n-\trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n-\t\trefs/tags/nested/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'is-base atom with non-commits' '\n-\tgit for-each-ref --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n-\tgrep \"(HEAD) refs/heads/main\" out &&\n-\n-\ttest_line_count = 2 err &&\n-\tgrep \"error: object .* is a commit, not a blob\" err &&\n-\tgrep \"error: bad tag pointer to\" err\n-'\n-\n-GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n-TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n-\n-test_expect_success GPG 'setup for signature atom using gpg' '\n-\tgit checkout -b signed &&\n-\n-\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n-\n-\techo \"1\" >file &&\n-\tgit add file &&\n-\ttest_tick &&\n-\tgit commit -S -m \"file: 1\" &&\n-\tgit tag first-signed &&\n-\n-\techo \"2\" >file &&\n-\ttest_tick &&\n-\tgit commit -a -m \"file: 2\" &&\n-\tgit tag second-unsigned &&\n-\n-\tgit config commit.gpgSign 1 &&\n-\techo \"3\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n-\tgit tag third-unsigned &&\n-\n-\ttest_tick &&\n-\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n-\tgit tag third-signed &&\n-\n-\techo \"4\" >file &&\n-\ttest_tick &&\n-\tgit commit -a -SB7227189 -m \"file: 4\" &&\n-\tgit tag fourth-signed &&\n-\n-\techo \"5\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n-\tgit tag fifth-unsigned &&\n-\n-\techo \"6\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n-\n-\ttest_tick &&\n-\tgit rebase -f HEAD^^ &&\n-\tgit tag fifth-signed HEAD^ &&\n-\tgit tag sixth-signed &&\n-\n-\techo \"7\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n-\tgit tag seventh-unsigned\n-'\n-\n-test_expect_success GPGSSH 'setup for signature atom using ssh' '\n-\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n-\n-\ttest_config gpg.format ssh &&\n-\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n-\techo \"8\" >file &&\n-\ttest_tick &&\n-\tgit add file &&\n-\tgit commit -S -m \"file: 8\" &&\n-\tgit tag eighth-signed-ssh\n-'\n-\n-test_expect_success GPG2 'bare signature atom' '\n-\tgit verify-commit first-signed 2>expect &&\n-\techo  >>expect &&\n-\tgit for-each-ref refs/tags/first-signed \\\n-\t\t--format=\"%(signature)\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show good signature with custom format' '\n-\tgit verify-commit first-signed &&\n-\tcat >expect <<-\\EOF &&\n-\tG\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\tEOF\n-\tgit for-each-ref refs/tags/first-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n-\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n-\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n-\tcat >expect.tmpl <<-\\EOF &&\n-\tG\n-\tFINGERPRINT\n-\tprincipal with number 1\n-\tFINGERPRINT\n-\n-\tEOF\n-\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n-\tgit for-each-ref refs/tags/eighth-signed-ssh \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'signature atom with grade option and bad signature' '\n-\tgit cat-file commit third-signed >raw &&\n-\tsed -e \"s/^file: 3/file: 3 forged/\" raw >forged1 &&\n-\tFORGED1=$(git hash-object -w -t commit forged1) &&\n-\tgit update-ref refs/tags/third-signed \"$FORGED1\" &&\n-\ttest_must_fail git verify-commit \"$FORGED1\" &&\n-\n-\tcat >expect <<-\\EOF &&\n-\tB\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\n-\n-\tEOF\n-\tgit for-each-ref refs/tags/third-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tU\n-\t65A0EEA02E30CAD7\n-\tEris Discordia <discord@example.net>\n-\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n-\tD4BE22311AD3131E5EDA29A461092E85B7227189\n-\tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with undefined trust level' '\n-\tcat >expect <<-\\EOF &&\n-\tundefined\n-\t65A0EEA02E30CAD7\n-\tEris Discordia <discord@example.net>\n-\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n-\tD4BE22311AD3131E5EDA29A461092E85B7227189\n-\tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n-\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n-\tcat >expect <<-\\EOF &&\n-\tultimate\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\tEOF\n-\tgit for-each-ref refs/tags/sixth-signed \\\n-\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show unknown signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tE\n-\t13B6F51ECDDE430D\n-\n-\n-\n-\tEOF\n-\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n-\t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show lack of signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tN\n-\n-\n-\n-\n-\tEOF\n-\tgit for-each-ref refs/tags/seventh-unsigned \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n+. \"$TEST_DIRECTORY\"/for-each-ref-tests.sh\n \n test_done\n-- \n2.34.1\n\n"},{"id":"523407","messageId":"20250804092255.1092973-7-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250804092255.1092973-1-meetsoni3017@gmail.com","subject":"[GSoC][RFC PATCH v5 6/6] t: add test for git refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-04T09:22:55Z","receivedAt":"2025-08-04T09:23:31Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Add a test script, `t/t1461-refs-list.sh`, for the new `git refs list`\ncommand.\n\nThis script acts as a simple driver, leveraging the shared test library\ncreated in the preceding commit. It works by overriding the\n`$git_for_each_ref` variable to \"git refs list\" and then sourcing the\nshared library (`t/for-each-ref-tests.sh`).\n\nThis approach ensures that `git refs list` is tested against the\nentire comprehensive test suite of `git for-each-ref`, verifying\nthat it acts as a compatible drop-in replacement.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/meson.build        | 1 +\n t/t1461-refs-list.sh | 8 ++++++++\n 2 files changed, 9 insertions(+)\n create mode 100755 t/t1461-refs-list.sh\n\ndiff --git a/t/meson.build b/t/meson.build\nindex 50e89e764a..c959c039d0 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -224,6 +224,7 @@ integration_tests = [\n   't1450-fsck.sh',\n   't1451-fsck-buffer.sh',\n   't1460-refs-migrate.sh',\n+  't1461-refs-list.sh',\n   't1500-rev-parse.sh',\n   't1501-work-tree.sh',\n   't1502-rev-parse-parseopt.sh',\ndiff --git a/t/t1461-refs-list.sh b/t/t1461-refs-list.sh\nnew file mode 100755\nindex 0000000000..36e3d81e59\n--- /dev/null\n+++ b/t/t1461-refs-list.sh\n@@ -0,0 +1,8 @@\n+#!/bin/sh\n+\n+test_description='git refs list tests'\n+\n+. ./test-lib.sh\n+\n+git_for_each_ref='git refs list'\n+. \"$TEST_DIRECTORY\"/for-each-ref-tests.sh\n-- \n2.34.1\n\n"},{"id":"523409","messageId":"61933769-1992-473e-8d0b-8cd6946e80ce@gmail.com","threadId":"63703","inReplyTo":"c4d9b25a-2a62-41bf-90e6-455c62b03aa3@gmail.com","subject":"Re: [GSoC][RFC PATCH v4 3/5] builtin/refs: add list subcommand","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-08-04T09:27:56Z","receivedAt":"2025-08-04T09:28:01Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 01/08/2025 16:49, Phillip Wood wrote:\n> On 01/08/2025 15:43, Junio C Hamano wrote:\n>> Phillip Wood <phillip.wood123@gmail.com> writes:\n>>\n>> What does a double-asterisk currently do in these patterns?\n> \n> refs/heads/m** seems to behave like refs/heads/m*. I'm a bit surprised \n> by that as for-each-ref seems to set WM_PATHNAME and I thought that our \n> wildmatch code used '**' to match any character in that case.\n\nI'd forgotten the rules for '**' - it must come after a slash and be \nfollowed by a slash if it is not at the end of a pattern otherwise it is \nsilently converted to '*'. I wish our wildmatch code at least warned \nwhen it did that. So one can query all the branches beginning with \"m\" \nby passing\n\n     'refs/heads/m*' 'refs/heads/m*/**'\n\nwhich isn't as convenient as it could be but it is possible.\n\n>> If it\n>> is not doing anything useful, perhaps we should make it match any\n>> letter, without getting constrained by hierarchy boundaries?  IOW,\n>> a \"fix\" might be to make sure the following happens?\n>>\n>>   - \"refs/heads/m*\" matches all local branches whose name starts with\n>>     'm' like 'morning', but not the ones inside subhierarchies that\n>>     start with 'm' like 'mid/night'.\n>>\n>>   - \"refs/heads/m**\" matches all local branches whose name starts\n>>     with 'm' and in the ones inside subhierarchies that start with\n>>     'm'.\n> \n> That sounds like a good idea\n\nNow I'm not so sure. We could add a flag to wildmatch() that allows ** \nto match any character anywhere in a pattern (I think that is how the \ncode behaved when it was first imported from rsync and it also rejected \ninvalid patterns) but it if we use that flag in for-each-ref we'd be \nchanging the behavior.\n\nThanks\n\nPhillip\n\n"},{"id":"523410","messageId":"9544a669-3e3a-492f-a312-748b5d49bec3@gmail.com","threadId":"63703","inReplyTo":"xmqqwm7nklqf.fsf@gitster.g","subject":"Re: [GSoC][RFC PATCH v4 3/5] builtin/refs: add list subcommand","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-08-04T09:28:05Z","receivedAt":"2025-08-04T09:28:11Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 01/08/2025 18:14, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n> \n>> It is confusing that refs/heads does a prefix match but refs/heads/m\n>> does not (unless there is a hierarchy boundary after the m).\n> \n> And refs/hea would not show branches, for the same reason.  It is\n> what \"anchored at hierarchy boundary\" in the message you are\n> responding to means.  In other words, it is not a simple textual\n> prefix match.\n\nThe last sentence sums up why I find it confusing - it is not possible \nto tell from the pattern what sort of match will be returned. If a \nscript wants to query some property of a ref it either has to know it \nexists (which is racy but probably not a practical problem most of the \ntime) or it has to include %(refname) in the format argument and check \nthat it matches the ref in the query string. A script can force a prefix \nmatch by adding a trailing slash but there is no way to force an exact \nmatch.\n\nThanks\n\nPhillip\n"},{"id":"523453","messageId":"xmqqh5yn6qxg.fsf@gitster.g","threadId":"63703","inReplyTo":"61933769-1992-473e-8d0b-8cd6946e80ce@gmail.com","subject":"Re: [GSoC][RFC PATCH v4 3/5] builtin/refs: add list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-04T15:35:07Z","receivedAt":"2025-08-04T15:35:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> On 01/08/2025 16:49, Phillip Wood wrote:\n>> On 01/08/2025 15:43, Junio C Hamano wrote:\n>>> Phillip Wood <phillip.wood123@gmail.com> writes:\n>>>\n>>> What does a double-asterisk currently do in these patterns?\n>> refs/heads/m** seems to behave like refs/heads/m*. I'm a bit\n>> surprised by that as for-each-ref seems to set WM_PATHNAME and I\n>> thought that our wildmatch code used '**' to match any character in\n>> that case.\n>\n> I'd forgotten the rules for '**' - it must come after a slash and be\n> followed by a slash if it is not at the end of a pattern otherwise it\n> is silently converted to '*'. I wish our wildmatch code at least\n> warned when it did that. So one can query all the branches beginning\n> with \"m\" by passing\n>\n>     'refs/heads/m*' 'refs/heads/m*/**'\n>\n> which isn't as convenient as it could be but it is possible.\n\nAs long as the rules are consistent and understandable (once you\nunderstand it, that is), then I am perfectly fine.  And \"** is\nwritten as /**/ (but you can omit slashes at either ends)\" I find\nacceptable.\n\n>>>   - \"refs/heads/m*\" matches all local branches whose name starts with\n>>>     'm' like 'morning', but not the ones inside subhierarchies that\n>>>     start with 'm' like 'mid/night'.\n>>>\n>>>   - \"refs/heads/m**\" matches all local branches whose name starts\n>>>     with 'm' and in the ones inside subhierarchies that start with\n>>>     'm'.\n>> That sounds like a good idea\n>\n> Now I'm not so sure.\n\nAs long as the existing rule is serviceable (and you seem to have\nfound that it is), we do not need to make such a change.\n\nThanks for thinking it through.\n\n"},{"id":"523474","messageId":"xmqq1ppr5427.fsf@gitster.g","threadId":"63703","inReplyTo":"20250804092255.1092973-2-meetsoni3017@gmail.com","subject":"Re: [GSoC][RFC PATCH v5 1/6] doc: factor out common option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-04T18:34:24Z","receivedAt":"2025-08-04T18:34:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Meet Soni <meetsoni3017@gmail.com> writes:\n\n> In preparation for adding documentation for `git refs list`, factor out\n> the common options from the `git-for-each-ref` man page into a\n> shareable file `for-each-ref-options.adoc` and update\n> `git-for-each-ref.adoc` to use an `include::` macro.\n>\n> This change is a pure refactoring and results in no change to the\n> final rendered documentation for `for-each-ref`.\n>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Mentored-by: shejialuo <shejialuo@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Signed-off-by: Meet Soni <meetsoni3017@gmail.com>\n> ---\n>  Documentation/for-each-ref-options.adoc | 79 ++++++++++++++++++++++++\n>  Documentation/git-for-each-ref.adoc     | 80 +------------------------\n>  2 files changed, 80 insertions(+), 79 deletions(-)\n>  create mode 100644 Documentation/for-each-ref-options.adoc\n\nThis contradicts with Karthik's \"for-each-ref --start-after=<>\"\ntopic that has been in 'next' and now in 'master'.  Mechanically\nmerging the result of applying these patches on top of 'master'\nbefore the \"--start-after\" series into 'seen' would leave the\ndescription of --start-after in git-for-each-refs.adoc; there isn't\nany textual conflict, which makes it even worse.\n\nRebasing the series on top 2.51-rc0 may not be a bad idea.\n\nThanks.\n"},{"id":"523517","messageId":"20250805092758.5321-1-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250804092255.1092973-1-meetsoni3017@gmail.com","subject":"[GSoC][PATCH v6 0/6] Add refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-05T09:27:52Z","receivedAt":"2025-08-05T09:28:06Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Hello everyone,\n\nThis is the sixth version of the patch series that introduces the git\nrefs list subcommand.\n\nchanges in v6:\n  - rebased onto v2.51.0-rc0\n\nMeet Soni (6):\n  doc: factor out common option\n  builtin/for-each-ref: align usage string with the man page\n  builtin/for-each-ref: factor out core logic into a helper\n  builtin/refs: add list subcommand\n  t6300: refactor tests to be shareable\n  t: add test for git refs list subcommand\n\n Documentation/for-each-ref-options.adoc |   88 +\n Documentation/git-for-each-ref.adoc     |   89 +-\n Documentation/git-refs.adoc             |   16 +\n builtin/for-each-ref.c                  |   36 +-\n builtin/refs.c                          |   14 +\n for-each-ref.h                          |   26 +\n t/for-each-ref-tests.sh                 | 2141 +++++++++++++++++++++++\n t/meson.build                           |    1 +\n t/t0450/adoc-help-mismatches            |    1 -\n t/t1461-refs-list.sh                    |    8 +\n t/t6300-for-each-ref.sh                 | 2140 +---------------------\n 11 files changed, 2316 insertions(+), 2244 deletions(-)\n create mode 100644 Documentation/for-each-ref-options.adoc\n create mode 100644 for-each-ref.h\n create mode 100644 t/for-each-ref-tests.sh\n create mode 100755 t/t1461-refs-list.sh\n\nRange-diff against v5:\n1:  d2fa47a2b9 ! 1:  fd4959594c doc: factor out common option\n    @@ Documentation/for-each-ref-options.adoc (new)\n     +\n     +--include-root-refs::\n     +\tList root refs (HEAD and pseudorefs) apart from regular refs.\n    ++\n    ++--start-after=<marker>::\n    ++    Allows paginating the output by skipping references up to and including the\n    ++    specified marker. When paging, it should be noted that references may be\n    ++    deleted, modified or added between invocations. Output will only yield those\n    ++    references which follow the marker lexicographically. Output begins from the\n    ++    first reference that would come after the marker alphabetically. Cannot be\n    ++    used with `--sort=<key>` or `--stdin` options, or the _<pattern>_ argument(s)\n    ++    to limit the refs.\n     \n      ## Documentation/git-for-each-ref.adoc ##\n     @@ Documentation/git-for-each-ref.adoc: host language allowing their direct evaluation in that language.\n    @@ Documentation/git-for-each-ref.adoc: host language allowing their direct evaluat\n     -\n     ---include-root-refs::\n     -\tList root refs (HEAD and pseudorefs) apart from regular refs.\n    +-\n    +---start-after=<marker>::\n    +-    Allows paginating the output by skipping references up to and including the\n    +-    specified marker. When paging, it should be noted that references may be\n    +-    deleted, modified or added between invocations. Output will only yield those\n    +-    references which follow the marker lexicographically. Output begins from the\n    +-    first reference that would come after the marker alphabetically. Cannot be\n    +-    used with `--sort=<key>` or `--stdin` options, or the _<pattern>_ argument(s)\n    +-    to limit the refs.\n     +include::for-each-ref-options.adoc[]\n      \n      FIELD NAMES\n2:  48a006dca9 ! 2:  1ed0717f6b builtin/for-each-ref: align usage string with the man page\n    @@ builtin/for-each-ref.c\n     +#define COMMON_USAGE_FOR_EACH_REF \\\n     +\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n     +\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n    -+\t\"                         [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n    -+\t\"                         [--points-at=<object>]\\n\" \\\n    ++\t\"                         [--include-root-refs] [--points-at=<object>]\\n\" \\\n     +\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n     +\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n    -+\t\"                         [--exclude=<pattern> ...]\"\n    ++\t\"                         [(--exclude=<pattern>)...] [--start-after=<marker>]\\n\" \\\n    ++\t\"                         [ --stdin | <pattern>... ]\"\n     +\n      static char const * const for_each_ref_usage[] = {\n     -\tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n     -\tN_(\"git for-each-ref [--points-at <object>]\"),\n     -\tN_(\"git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]\"),\n     -\tN_(\"git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]\"),\n    +-\tN_(\"git for-each-ref [--start-after <marker>]\"),\n     +\t\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF,\n      \tNULL\n      };\n3:  b7788d477a ! 3:  69f147aa12 builtin/for-each-ref: factor out core logic into a helper\n    @@ Commit message\n     \n      ## builtin/for-each-ref.c ##\n     @@\n    - #include \"builtin.h\"\n      #include \"commit.h\"\n      #include \"config.h\"\n    + #include \"environment.h\"\n     +#include \"for-each-ref.h\"\n      #include \"gettext.h\"\n      #include \"object.h\"\n    @@ builtin/for-each-ref.c\n     -#define COMMON_USAGE_FOR_EACH_REF \\\n     -\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n     -\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n    --\t\"                         [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n    --\t\"                         [--points-at=<object>]\\n\" \\\n    +-\t\"                         [--include-root-refs] [--points-at=<object>]\\n\" \\\n     -\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n     -\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n    --\t\"                         [--exclude=<pattern> ...]\"\n    +-\t\"                         [(--exclude=<pattern>)...] [--start-after=<marker>]\\n\" \\\n    +-\t\"                         [ --stdin | <pattern>... ]\"\n     -\n     -static char const * const for_each_ref_usage[] = {\n     -\t\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF,\n    @@ builtin/for-each-ref.c: int cmd_for_each_ref(int argc,\n     -\t\tusage_with_options(for_each_ref_usage, opts);\n     +\t\tusage_with_options(usage, opts);\n      \n    - \tsorting = ref_sorting_options(&sorting_options);\n    - \tref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);\n    + \tif (filter.start_after && sorting_options.nr > 1)\n    + \t\tdie(_(\"cannot use --start-after with custom sort options\"));\n     @@ builtin/for-each-ref.c: int cmd_for_each_ref(int argc,\n      \tstrvec_clear(&vec);\n      \treturn 0;\n    @@ for-each-ref.h (new)\n     +#define COMMON_USAGE_FOR_EACH_REF \\\n     +\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n     +\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n    -+\t\"                         [--include-root-refs] [ --stdin | <pattern>... ]\\n\" \\\n    -+\t\"                         [--points-at=<object>]\\n\" \\\n    ++\t\"                         [--include-root-refs] [--points-at=<object>]\\n\" \\\n     +\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n     +\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n    -+\t\"                         [--exclude=<pattern> ...]\"\n    ++\t\"                         [(--exclude=<pattern>)...] [--start-after=<marker>]\\n\" \\\n    ++\t\"                         [ --stdin | <pattern>... ]\"\n     +\n     +/*\n     + * The core logic for for-each-ref and its clones.\n4:  97088dab96 ! 4:  4195415eb5 builtin/refs: add list subcommand\n    @@ Documentation/git-refs.adoc: SYNOPSIS\n      git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n      git refs verify [--strict] [--verbose]\n     +git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n    -+\t      [(--sort=<key>)...] [--format=<format>]\n    -+\t      [--include-root-refs] [ --stdin | <pattern>... ]\n    -+\t      [--points-at=<object>]\n    -+\t      [--merged[=<object>]] [--no-merged[=<object>]]\n    -+\t      [--contains[=<object>]] [--no-contains[=<object>]]\n    -+\t      [--exclude=<pattern> ...]\n    ++\t\t   [(--sort=<key>)...] [--format=<format>]\n    ++\t\t   [--include-root-refs] [--points-at=<object>]\n    ++\t\t   [--merged[=<object>]] [--no-merged[=<object>]]\n    ++\t\t   [--contains[=<object>]] [--no-contains[=<object>]]\n    ++\t\t   [(--exclude=<pattern>)...] [--start-after=<marker>]\n    ++\t\t   [ --stdin | <pattern>... ]\n      \n      DESCRIPTION\n      -----------\n5:  abe9df9c4f = 5:  d3da47e950 t6300: refactor tests to be shareable\n6:  a037a47dcd = 6:  df2c3fc720 t: add test for git refs list subcommand\n-- \n2.34.1\n\n"},{"id":"523518","messageId":"20250805092758.5321-2-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250805092758.5321-1-meetsoni3017@gmail.com","subject":"[GSoC][PATCH v6 1/6] doc: factor out common option","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-05T09:27:53Z","receivedAt":"2025-08-05T09:28:11Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"In preparation for adding documentation for `git refs list`, factor out\nthe common options from the `git-for-each-ref` man page into a\nshareable file `for-each-ref-options.adoc` and update\n`git-for-each-ref.adoc` to use an `include::` macro.\n\nThis change is a pure refactoring and results in no change to the\nfinal rendered documentation for `for-each-ref`.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/for-each-ref-options.adoc | 88 ++++++++++++++++++++++++\n Documentation/git-for-each-ref.adoc     | 89 +------------------------\n 2 files changed, 89 insertions(+), 88 deletions(-)\n create mode 100644 Documentation/for-each-ref-options.adoc\n\ndiff --git a/Documentation/for-each-ref-options.adoc b/Documentation/for-each-ref-options.adoc\nnew file mode 100644\nindex 0000000000..4a033d3e16\n--- /dev/null\n+++ b/Documentation/for-each-ref-options.adoc\n@@ -0,0 +1,88 @@\n+<pattern>...::\n+\tIf one or more patterns are given, only refs are shown that\n+\tmatch against at least one pattern, either using fnmatch(3) or\n+\tliterally, in the latter case matching completely or from the\n+\tbeginning up to a slash.\n+\n+--stdin::\n+\tIf `--stdin` is supplied, then the list of patterns is read from\n+\tstandard input instead of from the argument list.\n+\n+--count=<count>::\n+\tBy default the command shows all refs that match\n+\t`<pattern>`.  This option makes it stop after showing\n+\tthat many refs.\n+\n+--sort=<key>::\n+\tA field name to sort on.  Prefix `-` to sort in\n+\tdescending order of the value.  When unspecified,\n+\t`refname` is used.  You may use the --sort=<key> option\n+\tmultiple times, in which case the last key becomes the primary\n+\tkey.\n+\n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from a ref being shown and\n+\tthe object it points at. In addition, the string literal `%%`\n+\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n+\tthe character with hex code `xx`. For example, `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n++\n+When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n+TAB %(refname)`.\n+\n+--color[=<when>]::\n+\tRespect any colors specified in the `--format` option. The\n+\t`<when>` field must be one of `always`, `never`, or `auto` (if\n+\t`<when>` is absent, behave as if `always` was given).\n+\n+--shell::\n+--perl::\n+--python::\n+--tcl::\n+\tIf given, strings that substitute `%(fieldname)`\n+\tplaceholders are quoted as string literals suitable for\n+\tthe specified host language.  This is meant to produce\n+\ta scriptlet that can directly be `eval`ed.\n+\n+--points-at=<object>::\n+\tOnly list refs which points at the given object.\n+\n+--merged[=<object>]::\n+\tOnly list refs whose tips are reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--no-merged[=<object>]::\n+\tOnly list refs whose tips are not reachable from the\n+\tspecified commit (HEAD if not specified).\n+\n+--contains[=<object>]::\n+\tOnly list refs which contain the specified commit (HEAD if not\n+\tspecified).\n+\n+--no-contains[=<object>]::\n+\tOnly list refs which don't contain the specified commit (HEAD\n+\tif not specified).\n+\n+--ignore-case::\n+\tSorting and filtering refs are case insensitive.\n+\n+--omit-empty::\n+\tDo not print a newline after formatted refs where the format expands\n+\tto the empty string.\n+\n+--exclude=<pattern>::\n+\tIf one or more patterns are given, only refs which do not match\n+\tany excluded pattern(s) are shown. Matching is done using the\n+\tsame rules as `<pattern>` above.\n+\n+--include-root-refs::\n+\tList root refs (HEAD and pseudorefs) apart from regular refs.\n+\n+--start-after=<marker>::\n+    Allows paginating the output by skipping references up to and including the\n+    specified marker. When paging, it should be noted that references may be\n+    deleted, modified or added between invocations. Output will only yield those\n+    references which follow the marker lexicographically. Output begins from the\n+    first reference that would come after the marker alphabetically. Cannot be\n+    used with `--sort=<key>` or `--stdin` options, or the _<pattern>_ argument(s)\n+    to limit the refs.\ndiff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc\nindex 060940904d..130d452de0 100644\n--- a/Documentation/git-for-each-ref.adoc\n+++ b/Documentation/git-for-each-ref.adoc\n@@ -28,94 +28,7 @@ host language allowing their direct evaluation in that language.\n \n OPTIONS\n -------\n-<pattern>...::\n-\tIf one or more patterns are given, only refs are shown that\n-\tmatch against at least one pattern, either using fnmatch(3) or\n-\tliterally, in the latter case matching completely or from the\n-\tbeginning up to a slash.\n-\n---stdin::\n-\tIf `--stdin` is supplied, then the list of patterns is read from\n-\tstandard input instead of from the argument list.\n-\n---count=<count>::\n-\tBy default the command shows all refs that match\n-\t`<pattern>`.  This option makes it stop after showing\n-\tthat many refs.\n-\n---sort=<key>::\n-\tA field name to sort on.  Prefix `-` to sort in\n-\tdescending order of the value.  When unspecified,\n-\t`refname` is used.  You may use the --sort=<key> option\n-\tmultiple times, in which case the last key becomes the primary\n-\tkey.\n-\n---format=<format>::\n-\tA string that interpolates `%(fieldname)` from a ref being shown and\n-\tthe object it points at. In addition, the string literal `%%`\n-\trenders as `%` and `%xx` - where `xx` are hex digits - renders as\n-\tthe character with hex code `xx`. For example, `%00` interpolates to\n-\t`\\0` (NUL), `%09` to `\\t` (TAB), and `%0a` to `\\n` (LF).\n-+\n-When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)\n-TAB %(refname)`.\n-\n---color[=<when>]::\n-\tRespect any colors specified in the `--format` option. The\n-\t`<when>` field must be one of `always`, `never`, or `auto` (if\n-\t`<when>` is absent, behave as if `always` was given).\n-\n---shell::\n---perl::\n---python::\n---tcl::\n-\tIf given, strings that substitute `%(fieldname)`\n-\tplaceholders are quoted as string literals suitable for\n-\tthe specified host language.  This is meant to produce\n-\ta scriptlet that can directly be `eval`ed.\n-\n---points-at=<object>::\n-\tOnly list refs which points at the given object.\n-\n---merged[=<object>]::\n-\tOnly list refs whose tips are reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---no-merged[=<object>]::\n-\tOnly list refs whose tips are not reachable from the\n-\tspecified commit (HEAD if not specified).\n-\n---contains[=<object>]::\n-\tOnly list refs which contain the specified commit (HEAD if not\n-\tspecified).\n-\n---no-contains[=<object>]::\n-\tOnly list refs which don't contain the specified commit (HEAD\n-\tif not specified).\n-\n---ignore-case::\n-\tSorting and filtering refs are case insensitive.\n-\n---omit-empty::\n-\tDo not print a newline after formatted refs where the format expands\n-\tto the empty string.\n-\n---exclude=<pattern>::\n-\tIf one or more patterns are given, only refs which do not match\n-\tany excluded pattern(s) are shown. Matching is done using the\n-\tsame rules as `<pattern>` above.\n-\n---include-root-refs::\n-\tList root refs (HEAD and pseudorefs) apart from regular refs.\n-\n---start-after=<marker>::\n-    Allows paginating the output by skipping references up to and including the\n-    specified marker. When paging, it should be noted that references may be\n-    deleted, modified or added between invocations. Output will only yield those\n-    references which follow the marker lexicographically. Output begins from the\n-    first reference that would come after the marker alphabetically. Cannot be\n-    used with `--sort=<key>` or `--stdin` options, or the _<pattern>_ argument(s)\n-    to limit the refs.\n+include::for-each-ref-options.adoc[]\n \n FIELD NAMES\n -----------\n-- \n2.34.1\n\n"},{"id":"523519","messageId":"20250805092758.5321-3-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250805092758.5321-1-meetsoni3017@gmail.com","subject":"[GSoC][PATCH v6 2/6] builtin/for-each-ref: align usage string with the man page","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-05T09:27:54Z","receivedAt":"2025-08-05T09:28:15Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Usage string for `git for-each-ref` was out of sync with its official\ndocumentation. The test `t0450-txt-doc-vs-help.sh` was marked as broken\ndue to this.\n\nUpdate the usage string to match the documentation. This allows the test\nto pass, so remove the corresponding 'known breakage' marker from the\ntest file.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n builtin/for-each-ref.c       | 15 ++++++++++-----\n t/t0450/adoc-help-mismatches |  1 -\n 2 files changed, 10 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 8b5fe7b65e..fe62f07861 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -9,12 +9,17 @@\n #include \"strbuf.h\"\n #include \"strvec.h\"\n \n+#define COMMON_USAGE_FOR_EACH_REF \\\n+\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t\"                         [--include-root-refs] [--points-at=<object>]\\n\" \\\n+\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t\"                         [(--exclude=<pattern>)...] [--start-after=<marker>]\\n\" \\\n+\t\"                         [ --stdin | <pattern>... ]\"\n+\n static char const * const for_each_ref_usage[] = {\n-\tN_(\"git for-each-ref [<options>] [<pattern>]\"),\n-\tN_(\"git for-each-ref [--points-at <object>]\"),\n-\tN_(\"git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]\"),\n-\tN_(\"git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]\"),\n-\tN_(\"git for-each-ref [--start-after <marker>]\"),\n+\t\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF,\n \tNULL\n };\n \ndiff --git a/t/t0450/adoc-help-mismatches b/t/t0450/adoc-help-mismatches\nindex 06b469bdee..2c6ecd5fc8 100644\n--- a/t/t0450/adoc-help-mismatches\n+++ b/t/t0450/adoc-help-mismatches\n@@ -17,7 +17,6 @@ fast-export\n fast-import\n fetch-pack\n fmt-merge-msg\n-for-each-ref\n format-patch\n fsck-objects\n fsmonitor--daemon\n-- \n2.34.1\n\n"},{"id":"523520","messageId":"20250805092758.5321-4-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250805092758.5321-1-meetsoni3017@gmail.com","subject":"[GSoC][PATCH v6 3/6] builtin/for-each-ref: factor out core logic into a helper","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-05T09:27:55Z","receivedAt":"2025-08-05T09:28:19Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"The implementation of `git for-each-ref` is monolithic within\n`cmd_for_each_ref()`, making it impossible to share its logic with other\ncommands. To enable code reuse for the upcoming `git refs list`\nsubcommand, refactor the core logic into a shared helper function.\n\nIntroduce a new `for-each-ref.h` header to define the public interface\nfor this shared logic. It contains the declaration for a new helper\nfunction, `for_each_ref_core()`, and a macro for the common usage\noptions.\n\nMove the option parsing, filtering, and formatting logic from\n`cmd_for_each_ref()` into a new helper function named\n`for_each_ref_core()`. This helper is made generic by accepting the\ncommand's usage string as a parameter.\n\nThe original `cmd_for_each_ref()` is simplified to a thin wrapper that\nis only responsible for defining its specific usage array and calling\nthe shared helper.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n builtin/for-each-ref.c | 41 +++++++++++++++++++----------------------\n for-each-ref.h         | 26 ++++++++++++++++++++++++++\n 2 files changed, 45 insertions(+), 22 deletions(-)\n create mode 100644 for-each-ref.h\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex fe62f07861..4af33de576 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -2,6 +2,7 @@\n #include \"commit.h\"\n #include \"config.h\"\n #include \"environment.h\"\n+#include \"for-each-ref.h\"\n #include \"gettext.h\"\n #include \"object.h\"\n #include \"parse-options.h\"\n@@ -9,24 +10,7 @@\n #include \"strbuf.h\"\n #include \"strvec.h\"\n \n-#define COMMON_USAGE_FOR_EACH_REF \\\n-\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n-\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n-\t\"                         [--include-root-refs] [--points-at=<object>]\\n\" \\\n-\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n-\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n-\t\"                         [(--exclude=<pattern>)...] [--start-after=<marker>]\\n\" \\\n-\t\"                         [ --stdin | <pattern>... ]\"\n-\n-static char const * const for_each_ref_usage[] = {\n-\t\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF,\n-\tNULL\n-};\n-\n-int cmd_for_each_ref(int argc,\n-\t\t     const char **argv,\n-\t\t     const char *prefix,\n-\t\t     struct repository *repo)\n+int for_each_ref_core(int argc, const char **argv, const char *prefix, struct repository *repo, const char *const *usage)\n {\n \tstruct ref_sorting *sorting;\n \tstruct string_list sorting_options = STRING_LIST_INIT_DUP;\n@@ -75,17 +59,17 @@ int cmd_for_each_ref(int argc,\n \t/* Set default (refname) sorting */\n \tstring_list_append(&sorting_options, \"refname\");\n \n-\tparse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);\n+\tparse_options(argc, argv, prefix, opts, usage, 0);\n \tif (format.array_opts.max_count < 0) {\n \t\terror(\"invalid --count argument: `%d'\", format.array_opts.max_count);\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(usage, opts);\n \t}\n \tif (HAS_MULTI_BITS(format.quote_style)) {\n \t\terror(\"more than one quoting style?\");\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(usage, opts);\n \t}\n \tif (verify_ref_format(&format))\n-\t\tusage_with_options(for_each_ref_usage, opts);\n+\t\tusage_with_options(usage, opts);\n \n \tif (filter.start_after && sorting_options.nr > 1)\n \t\tdie(_(\"cannot use --start-after with custom sort options\"));\n@@ -125,3 +109,16 @@ int cmd_for_each_ref(int argc,\n \tstrvec_clear(&vec);\n \treturn 0;\n }\n+\n+int cmd_for_each_ref(int argc,\n+\t\t     const char **argv,\n+\t\t     const char *prefix,\n+\t\t     struct repository *repo)\n+{\n+\tstatic char const * const for_each_ref_usage[] = {\n+\t\tN_(\"git for-each-ref \" COMMON_USAGE_FOR_EACH_REF),\n+\t\tNULL\n+\t};\n+\n+\treturn for_each_ref_core(argc, argv, prefix, repo, for_each_ref_usage);\n+}\ndiff --git a/for-each-ref.h b/for-each-ref.h\nnew file mode 100644\nindex 0000000000..a5e0b6d17a\n--- /dev/null\n+++ b/for-each-ref.h\n@@ -0,0 +1,26 @@\n+#ifndef FOR_EACH_REF_H\n+#define FOR_EACH_REF_H\n+\n+struct repository;\n+\n+/*\n+ * Shared usage string for options common to git-for-each-ref(1)\n+ * and git-refs-list(1). The command-specific part (e.g., \"git refs list \")\n+ * must be prepended by the caller.\n+ */\n+#define COMMON_USAGE_FOR_EACH_REF \\\n+\t\"[--count=<count>] [--shell|--perl|--python|--tcl]\\n\" \\\n+\t\"                         [(--sort=<key>)...] [--format=<format>]\\n\" \\\n+\t\"                         [--include-root-refs] [--points-at=<object>]\\n\" \\\n+\t\"                         [--merged[=<object>]] [--no-merged[=<object>]]\\n\" \\\n+\t\"                         [--contains[=<object>]] [--no-contains[=<object>]]\\n\" \\\n+\t\"                         [(--exclude=<pattern>)...] [--start-after=<marker>]\\n\" \\\n+\t\"                         [ --stdin | <pattern>... ]\"\n+\n+/*\n+ * The core logic for for-each-ref and its clones.\n+ */\n+int for_each_ref_core(int argc, const char **argv, const char *prefix,\n+\t\t      struct repository *repo, const char *const *usage);\n+\n+#endif /* FOR_EACH_REF_H */\n-- \n2.34.1\n\n"},{"id":"523521","messageId":"20250805092758.5321-5-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250805092758.5321-1-meetsoni3017@gmail.com","subject":"[GSoC][PATCH v6 4/6] builtin/refs: add list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-05T09:27:56Z","receivedAt":"2025-08-05T09:28:23Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Git's reference management is distributed across multiple commands. As\npart of an ongoing effort to consolidate and modernize reference\nhandling, introduce a `list` subcommand under the `git refs` umbrella as\na replacement for `git for-each-ref`.\n\nImplement `cmd_refs_list` by having it call the `for_each_ref_core()`\nhelper function. This helper was factored out of the original\n`cmd_for_each_ref` in a preceding commit, allowing both commands to\nshare the same core logic as independent peers.\n\nAdd documentation for the new command. The man page leverages the shared\noptions file, created in a previous commit, by using the AsciiDoc\n`include::` macro to ensure consistency with git-for-each-ref(1).\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n Documentation/git-refs.adoc | 16 ++++++++++++++++\n builtin/refs.c              | 14 ++++++++++++++\n 2 files changed, 30 insertions(+)\n\ndiff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc\nindex 4d6dc994f9..e608980711 100644\n--- a/Documentation/git-refs.adoc\n+++ b/Documentation/git-refs.adoc\n@@ -11,6 +11,13 @@ SYNOPSIS\n [synopsis]\n git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\n git refs verify [--strict] [--verbose]\n+git refs list [--count=<count>] [--shell|--perl|--python|--tcl]\n+\t\t   [(--sort=<key>)...] [--format=<format>]\n+\t\t   [--include-root-refs] [--points-at=<object>]\n+\t\t   [--merged[=<object>]] [--no-merged[=<object>]]\n+\t\t   [--contains[=<object>]] [--no-contains[=<object>]]\n+\t\t   [(--exclude=<pattern>)...] [--start-after=<marker>]\n+\t\t   [ --stdin | <pattern>... ]\n \n DESCRIPTION\n -----------\n@@ -26,6 +33,11 @@ migrate::\n verify::\n \tVerify reference database consistency.\n \n+list::\n+\tList references in the repository with support for filtering,\n+\tformatting, and sorting. This subcommand is an alias for\n+\tlinkgit:git-for-each-ref[1] and offers identical functionality.\n+\n OPTIONS\n -------\n \n@@ -57,6 +69,10 @@ The following options are specific to 'git refs verify':\n --verbose::\n \tWhen verifying the reference database consistency, be chatty.\n \n+The following options are specific to 'git refs list':\n+\n+include::for-each-ref-options.adoc[]\n+\n KNOWN LIMITATIONS\n -----------------\n \ndiff --git a/builtin/refs.c b/builtin/refs.c\nindex c7ad0a2963..76224feba4 100644\n--- a/builtin/refs.c\n+++ b/builtin/refs.c\n@@ -6,6 +6,7 @@\n #include \"refs.h\"\n #include \"strbuf.h\"\n #include \"worktree.h\"\n+#include \"for-each-ref.h\"\n \n #define REFS_MIGRATE_USAGE \\\n \tN_(\"git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]\")\n@@ -101,6 +102,17 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,\n \treturn ret;\n }\n \n+static int cmd_refs_list(int argc, const char **argv, const char *prefix,\n+\t\t\t   struct repository *repo)\n+{\n+\tstatic char const * const refs_list_usage[] = {\n+\t\tN_(\"git refs list \" COMMON_USAGE_FOR_EACH_REF),\n+\t\tNULL\n+\t};\n+\n+\treturn for_each_ref_core(argc, argv, prefix, repo, refs_list_usage);\n+}\n+\n int cmd_refs(int argc,\n \t     const char **argv,\n \t     const char *prefix,\n@@ -109,12 +121,14 @@ int cmd_refs(int argc,\n \tconst char * const refs_usage[] = {\n \t\tREFS_MIGRATE_USAGE,\n \t\tREFS_VERIFY_USAGE,\n+\t\t\"git refs list \" COMMON_USAGE_FOR_EACH_REF,\n \t\tNULL,\n \t};\n \tparse_opt_subcommand_fn *fn = NULL;\n \tstruct option opts[] = {\n \t\tOPT_SUBCOMMAND(\"migrate\", &fn, cmd_refs_migrate),\n \t\tOPT_SUBCOMMAND(\"verify\", &fn, cmd_refs_verify),\n+\t\tOPT_SUBCOMMAND(\"list\", &fn, cmd_refs_list),\n \t\tOPT_END(),\n \t};\n \n-- \n2.34.1\n\n"},{"id":"523523","messageId":"20250805092758.5321-6-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250805092758.5321-1-meetsoni3017@gmail.com","subject":"[GSoC][PATCH v6 5/6] t6300: refactor tests to be shareable","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-05T09:27:57Z","receivedAt":"2025-08-05T09:28:30Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"In preparation for adding tests for the new `git refs list` command,\nrefactor the existing t6300 test suite to make its logic shareable.\n\nMove the core test logic from `t6300-for-each-ref.sh` into a new\n`for-each-ref-tests.sh` file. Inside this new script, replace hardcoded\ncalls to \"git for-each-ref\" with the `$git_for_each_ref` variable.\n\nThe original `t6300-for-each-ref.sh` script now becomes a simple\n\"driver\". It is responsible for setting the default value of the\nvariable and then sourcing the test library.\n\nThis new structure follows the established pattern used for sharing\ntests between `git-blame` and `git-annotate` and prepares the test suite\nfor the `refs list` tests to be added in a subsequent commit.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/for-each-ref-tests.sh | 2141 +++++++++++++++++++++++++++++++++++++++\n t/t6300-for-each-ref.sh | 2140 +-------------------------------------\n 2 files changed, 2143 insertions(+), 2138 deletions(-)\n create mode 100644 t/for-each-ref-tests.sh\n\ndiff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh\nnew file mode 100644\nindex 0000000000..e3ad19298a\n--- /dev/null\n+++ b/t/for-each-ref-tests.sh\n@@ -0,0 +1,2141 @@\n+git_for_each_ref=${git_for_each_ref:-git for-each-ref}\n+GNUPGHOME_NOT_USED=$GNUPGHOME\n+. \"$TEST_DIRECTORY\"/lib-gpg.sh\n+. \"$TEST_DIRECTORY\"/lib-terminal.sh\n+\n+# Mon Jul 3 23:18:43 2006 +0000\n+datestamp=1151968723\n+setdate_and_increment () {\n+    GIT_COMMITTER_DATE=\"$datestamp +0200\"\n+    datestamp=$(expr \"$datestamp\" + 1)\n+    GIT_AUTHOR_DATE=\"$datestamp +0200\"\n+    datestamp=$(expr \"$datestamp\" + 1)\n+    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n+}\n+\n+test_object_file_size () {\n+\toid=$(git rev-parse \"$1\")\n+\tpath=\".git/objects/$(test_oid_to_path $oid)\"\n+\ttest_file_size \"$path\"\n+}\n+\n+test_expect_success setup '\n+\t# setup .mailmap\n+\tcat >.mailmap <<-EOF &&\n+\tA Thor <athor@example.com> A U Thor <author@example.com>\n+\tC Mitter <cmitter@example.com> C O Mitter <committer@example.com>\n+\tEOF\n+\n+\tsetdate_and_increment &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Initial\" &&\n+\tgit branch -M main &&\n+\tsetdate_and_increment &&\n+\tgit tag -a -m \"Tagging at $datestamp\" testtag &&\n+\tgit update-ref refs/remotes/origin/main main &&\n+\tgit remote add origin nowhere &&\n+\tgit config branch.main.remote origin &&\n+\tgit config branch.main.merge refs/heads/main &&\n+\tgit remote add myfork elsewhere &&\n+\tgit config remote.pushdefault myfork &&\n+\tgit config push.default current\n+'\n+\n+test_atom () {\n+\tcase \"$1\" in\n+\t\thead) ref=refs/heads/main ;;\n+\t\t tag) ref=refs/tags/testtag ;;\n+\t\t sym) ref=refs/heads/sym ;;\n+\t\t   *) ref=$1 ;;\n+\tesac\n+\tformat=$2\n+\ttest_do=test_expect_${4:-success}\n+\n+\tprintf '%s\\n' \"$3\" >expected\n+\t$test_do $PREREQ \"basic atom: $ref $format\" '\n+\t\t${git_for_each_ref} --format=\"%($format)\" \"$ref\" >actual &&\n+\t\tsanitize_pgp <actual >actual.clean &&\n+\t\ttest_cmp expected actual.clean\n+\t'\n+\n+\t# Automatically test \"contents:size\" atom after testing \"contents\"\n+\tif test \"$format\" = \"contents\"\n+\tthen\n+\t\t# for commit leg, $3 is changed there\n+\t\texpect=$(printf '%s' \"$3\" | wc -c)\n+\t\t$test_do $PREREQ \"basic atom: $ref contents:size\" '\n+\t\t\ttype=$(git cat-file -t \"$ref\") &&\n+\t\t\tcase $type in\n+\t\t\ttag)\n+\t\t\t\t# We cannot use $3 as it expects sanitize_pgp to run\n+\t\t\t\tgit cat-file tag $ref >out &&\n+\t\t\t\texpect=$(tail -n +6 out | wc -c) &&\n+\t\t\t\trm -f out ;;\n+\t\t\ttree | blob)\n+\t\t\t\texpect=\"\" ;;\n+\t\t\tcommit)\n+\t\t\t\t: \"use the calculated expect\" ;;\n+\t\t\t*)\n+\t\t\t\tBUG \"unknown object type\" ;;\n+\t\t\tesac &&\n+\t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n+\t\t\techo $expect >expected &&\n+\t\t\t${git_for_each_ref} --format=\"%(contents:size)\" \"$ref\" >actual &&\n+\t\t\ttest_cmp expected actual\n+\t\t'\n+\tfi\n+}\n+\n+hexlen=$(test_oid hexsz)\n+\n+test_atom head refname refs/heads/main\n+test_atom head refname: refs/heads/main\n+test_atom head refname:short main\n+test_atom head refname:lstrip=1 heads/main\n+test_atom head refname:lstrip=2 main\n+test_atom head refname:lstrip=-1 main\n+test_atom head refname:lstrip=-2 heads/main\n+test_atom head refname:rstrip=1 refs/heads\n+test_atom head refname:rstrip=2 refs\n+test_atom head refname:rstrip=-1 refs\n+test_atom head refname:rstrip=-2 refs/heads\n+test_atom head refname:strip=1 heads/main\n+test_atom head refname:strip=2 main\n+test_atom head refname:strip=-1 main\n+test_atom head refname:strip=-2 heads/main\n+test_atom head upstream refs/remotes/origin/main\n+test_atom head upstream:short origin/main\n+test_atom head upstream:lstrip=2 origin/main\n+test_atom head upstream:lstrip=-2 origin/main\n+test_atom head upstream:rstrip=2 refs/remotes\n+test_atom head upstream:rstrip=-2 refs/remotes\n+test_atom head upstream:strip=2 origin/main\n+test_atom head upstream:strip=-2 origin/main\n+test_atom head push refs/remotes/myfork/main\n+test_atom head push:short myfork/main\n+test_atom head push:lstrip=1 remotes/myfork/main\n+test_atom head push:lstrip=-1 main\n+test_atom head push:rstrip=1 refs/remotes/myfork\n+test_atom head push:rstrip=-1 refs\n+test_atom head push:strip=1 remotes/myfork/main\n+test_atom head push:strip=-1 main\n+test_atom head objecttype commit\n+test_atom head objectsize $((131 + hexlen))\n+test_atom head objectsize:disk $(test_object_file_size refs/heads/main)\n+test_atom head deltabase $ZERO_OID\n+test_atom head objectname $(git rev-parse refs/heads/main)\n+test_atom head objectname:short $(git rev-parse --short refs/heads/main)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n+test_atom head tree $(git rev-parse refs/heads/main^{tree})\n+test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})\n+test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})\n+test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})\n+test_atom head parent ''\n+test_atom head parent:short ''\n+test_atom head parent:short=1 ''\n+test_atom head parent:short=10 ''\n+test_atom head numparent 0\n+test_atom head object ''\n+test_atom head type ''\n+test_atom head raw \"$(git cat-file commit refs/heads/main)\n+\"\n+test_atom head '*objectname' ''\n+test_atom head '*objecttype' ''\n+test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n+test_atom head authorname 'A U Thor'\n+test_atom head authorname:mailmap 'A Thor'\n+test_atom head authoremail '<author@example.com>'\n+test_atom head authoremail:trim 'author@example.com'\n+test_atom head authoremail:localpart 'author'\n+test_atom head authoremail:trim,localpart 'author'\n+test_atom head authoremail:mailmap '<athor@example.com>'\n+test_atom head authoremail:mailmap,trim 'athor@example.com'\n+test_atom head authoremail:trim,mailmap 'athor@example.com'\n+test_atom head authoremail:mailmap,localpart 'athor'\n+test_atom head authoremail:localpart,mailmap 'athor'\n+test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'\n+test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n+test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n+test_atom head committername 'C O Mitter'\n+test_atom head committername:mailmap 'C Mitter'\n+test_atom head committeremail '<committer@example.com>'\n+test_atom head committeremail:trim 'committer@example.com'\n+test_atom head committeremail:localpart 'committer'\n+test_atom head committeremail:localpart,trim 'committer'\n+test_atom head committeremail:mailmap '<cmitter@example.com>'\n+test_atom head committeremail:mailmap,trim 'cmitter@example.com'\n+test_atom head committeremail:trim,mailmap 'cmitter@example.com'\n+test_atom head committeremail:mailmap,localpart 'cmitter'\n+test_atom head committeremail:localpart,mailmap 'cmitter'\n+test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'\n+test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n+test_atom head tag ''\n+test_atom head tagger ''\n+test_atom head taggername ''\n+test_atom head taggeremail ''\n+test_atom head taggeremail:trim ''\n+test_atom head taggeremail:localpart ''\n+test_atom head taggerdate ''\n+test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n+test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n+test_atom head subject 'Initial'\n+test_atom head subject:sanitize 'Initial'\n+test_atom head contents:subject 'Initial'\n+test_atom head body ''\n+test_atom head contents:body ''\n+test_atom head contents:signature ''\n+test_atom head contents 'Initial\n+'\n+test_atom head HEAD '*'\n+\n+test_atom tag refname refs/tags/testtag\n+test_atom tag refname:short testtag\n+test_atom tag upstream ''\n+test_atom tag push ''\n+test_atom tag objecttype tag\n+test_atom tag objectsize $((114 + hexlen))\n+test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)\n+test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)\n+test_atom tag deltabase $ZERO_OID\n+test_atom tag '*deltabase' $ZERO_OID\n+test_atom tag objectname $(git rev-parse refs/tags/testtag)\n+test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n+test_atom tag tree ''\n+test_atom tag tree:short ''\n+test_atom tag tree:short=1 ''\n+test_atom tag tree:short=10 ''\n+test_atom tag parent ''\n+test_atom tag parent:short ''\n+test_atom tag parent:short=1 ''\n+test_atom tag parent:short=10 ''\n+test_atom tag numparent ''\n+test_atom tag object $(git rev-parse refs/tags/testtag^0)\n+test_atom tag type 'commit'\n+test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})\n+test_atom tag '*objecttype' 'commit'\n+test_atom tag author ''\n+test_atom tag authorname ''\n+test_atom tag authorname:mailmap ''\n+test_atom tag authoremail ''\n+test_atom tag authoremail:trim ''\n+test_atom tag authoremail:localpart ''\n+test_atom tag authoremail:trim,localpart ''\n+test_atom tag authoremail:mailmap ''\n+test_atom tag authoremail:mailmap,trim ''\n+test_atom tag authoremail:trim,mailmap ''\n+test_atom tag authoremail:mailmap,localpart ''\n+test_atom tag authoremail:localpart,mailmap ''\n+test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''\n+test_atom tag authordate ''\n+test_atom tag committer ''\n+test_atom tag committername ''\n+test_atom tag committername:mailmap ''\n+test_atom tag committeremail ''\n+test_atom tag committeremail:trim ''\n+test_atom tag committeremail:localpart ''\n+test_atom tag committeremail:localpart,trim ''\n+test_atom tag committeremail:mailmap ''\n+test_atom tag committeremail:mailmap,trim ''\n+test_atom tag committeremail:trim,mailmap ''\n+test_atom tag committeremail:mailmap,localpart ''\n+test_atom tag committeremail:localpart,mailmap ''\n+test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''\n+test_atom tag committerdate ''\n+test_atom tag tag 'testtag'\n+test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n+test_atom tag taggername 'C O Mitter'\n+test_atom tag taggername:mailmap 'C Mitter'\n+test_atom tag taggeremail '<committer@example.com>'\n+test_atom tag taggeremail:trim 'committer@example.com'\n+test_atom tag taggeremail:localpart 'committer'\n+test_atom tag taggeremail:trim,localpart 'committer'\n+test_atom tag taggeremail:mailmap '<cmitter@example.com>'\n+test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'\n+test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'\n+test_atom tag taggeremail:mailmap,localpart 'cmitter'\n+test_atom tag taggeremail:localpart,mailmap 'cmitter'\n+test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'\n+test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n+test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n+test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n+test_atom tag subject 'Tagging at 1151968727'\n+test_atom tag subject:sanitize 'Tagging-at-1151968727'\n+test_atom tag contents:subject 'Tagging at 1151968727'\n+test_atom tag body ''\n+test_atom tag contents:body ''\n+test_atom tag contents:signature ''\n+test_atom tag contents 'Tagging at 1151968727\n+'\n+test_atom tag HEAD ' '\n+\n+test_expect_success 'basic atom: refs/tags/testtag *raw' '\n+\tgit cat-file commit refs/tags/testtag^{} >expected &&\n+\t${git_for_each_ref} --format=\"%(*raw)\" refs/tags/testtag >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_expect_success 'Check invalid atoms names are errors' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(INVALID)\" refs/heads\n+'\n+\n+test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n+\t${git_for_each_ref} --format=\"%(authordate)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n+'\n+\n+test_expect_success 'Check valid format specifiers for date fields' '\n+\t${git_for_each_ref} --format=\"%(authordate:default)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:relative)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:short)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:local)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:iso8601)\" refs/heads &&\n+\t${git_for_each_ref} --format=\"%(authordate:rfc2822)\" refs/heads\n+'\n+\n+test_expect_success 'Check invalid format specifiers are errors' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(authordate:INVALID)\" refs/heads\n+'\n+\n+test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=0)\" &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=-1)\" &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(objectname:short=foo)\"\n+'\n+\n+test_bad_atom () {\n+\tcase \"$1\" in\n+\thead) ref=refs/heads/main ;;\n+\t tag) ref=refs/tags/testtag ;;\n+\t sym) ref=refs/heads/sym ;;\n+\t   *) ref=$1 ;;\n+\tesac\n+\tformat=$2\n+\ttest_do=test_expect_${4:-success}\n+\n+\tprintf '%s\\n' \"$3\" >expect\n+\t$test_do $PREREQ \"err basic atom: $ref $format\" '\n+\t\ttest_must_fail ${git_for_each_ref} \\\n+\t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n+\t\ttest_cmp expect error\n+\t'\n+}\n+\n+test_bad_atom head 'authoremail:foo' \\\n+\t'fatal: unrecognized %(authoremail) argument: foo'\n+\n+test_bad_atom head 'authoremail:mailmap,trim,bar' \\\n+\t'fatal: unrecognized %(authoremail) argument: bar'\n+\n+test_bad_atom head 'authoremail:trim,' \\\n+\t'fatal: unrecognized %(authoremail) argument: '\n+\n+test_bad_atom head 'authoremail:mailmaptrim' \\\n+\t'fatal: unrecognized %(authoremail) argument: trim'\n+\n+test_bad_atom head 'committeremail: ' \\\n+\t'fatal: unrecognized %(committeremail) argument:  '\n+\n+test_bad_atom head 'committeremail: trim,foo' \\\n+\t'fatal: unrecognized %(committeremail) argument:  trim,foo'\n+\n+test_bad_atom head 'committeremail:mailmap,localpart ' \\\n+\t'fatal: unrecognized %(committeremail) argument:  '\n+\n+test_bad_atom head 'committeremail:trim_localpart' \\\n+\t'fatal: unrecognized %(committeremail) argument: _localpart'\n+\n+test_bad_atom head 'committeremail:localpart,,,trim' \\\n+\t'fatal: unrecognized %(committeremail) argument: ,,trim'\n+\n+test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \\\n+\t'fatal: unrecognized %(taggeremail) argument:  foo '\n+\n+test_bad_atom tag 'taggeremail:trim,localpart,' \\\n+\t'fatal: unrecognized %(taggeremail) argument: '\n+\n+test_bad_atom tag 'taggeremail:mailmap;localpart trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument: ;localpart trim'\n+\n+test_bad_atom tag 'taggeremail:localpart trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument:  trim'\n+\n+test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \\\n+\t'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'\n+\n+test_date () {\n+\tf=$1 &&\n+\tcommitter_date=$2 &&\n+\tauthor_date=$3 &&\n+\ttagger_date=$4 &&\n+\tcat >expected <<-EOF &&\n+\t'refs/heads/main' '$committer_date' '$author_date'\n+\t'refs/tags/testtag' '$tagger_date'\n+\tEOF\n+\t(\n+\t\t${git_for_each_ref} --shell \\\n+\t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n+\t\t\trefs/heads &&\n+\t\t${git_for_each_ref} --shell \\\n+\t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n+\t\t\trefs/tags\n+\t) >actual &&\n+\ttest_cmp expected actual\n+}\n+\n+test_expect_success 'Check unformatted date fields output' '\n+\ttest_date \"\" \\\n+\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n+'\n+\n+test_expect_success 'Check format \"default\" formatted date fields output' '\n+\ttest_date default \\\n+\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n+\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n+'\n+\n+test_expect_success 'Check format \"default-local\" date fields output' '\n+\ttest_date default-local \"Mon Jul 3 23:18:43 2006\" \"Mon Jul 3 23:18:44 2006\" \"Mon Jul 3 23:18:45 2006\"\n+'\n+\n+# Don't know how to do relative check because I can't know when this script\n+# is going to be run and can't fake the current time to git, and hence can't\n+# provide expected output.  Instead, I'll just make sure that \"relative\"\n+# doesn't exit in error\n+test_expect_success 'Check format \"relative\" date fields output' '\n+\tf=relative &&\n+\t(${git_for_each_ref} --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n+\t${git_for_each_ref} --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n+'\n+\n+# We just check that this is the same as \"relative\" for now.\n+test_expect_success 'Check format \"relative-local\" date fields output' '\n+\ttest_date relative-local \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(committerdate:relative)\" refs/heads)\" \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(authordate:relative)\" refs/heads)\" \\\n+\t\t\"$(${git_for_each_ref} --format=\"%(taggerdate:relative)\" refs/tags)\"\n+'\n+\n+test_expect_success 'Check format \"short\" date fields output' '\n+\ttest_date short 2006-07-04 2006-07-04 2006-07-04\n+'\n+\n+test_expect_success 'Check format \"short-local\" date fields output' '\n+\ttest_date short-local 2006-07-03 2006-07-03 2006-07-03\n+'\n+\n+test_expect_success 'Check format \"local\" date fields output' '\n+\ttest_date local \\\n+\t\t\"Mon Jul 3 23:18:43 2006\" \\\n+\t\t\"Mon Jul 3 23:18:44 2006\" \\\n+\t\t\"Mon Jul 3 23:18:45 2006\"\n+'\n+\n+test_expect_success 'Check format \"iso8601\" date fields output' '\n+\ttest_date iso8601 \\\n+\t\t\"2006-07-04 01:18:43 +0200\" \\\n+\t\t\"2006-07-04 01:18:44 +0200\" \\\n+\t\t\"2006-07-04 01:18:45 +0200\"\n+'\n+\n+test_expect_success 'Check format \"iso8601-local\" date fields output' '\n+\ttest_date iso8601-local \"2006-07-03 23:18:43 +0000\" \"2006-07-03 23:18:44 +0000\" \"2006-07-03 23:18:45 +0000\"\n+'\n+\n+test_expect_success 'Check format \"rfc2822\" date fields output' '\n+\ttest_date rfc2822 \\\n+\t\t\"Tue, 4 Jul 2006 01:18:43 +0200\" \\\n+\t\t\"Tue, 4 Jul 2006 01:18:44 +0200\" \\\n+\t\t\"Tue, 4 Jul 2006 01:18:45 +0200\"\n+'\n+\n+test_expect_success 'Check format \"rfc2822-local\" date fields output' '\n+\ttest_date rfc2822-local \"Mon, 3 Jul 2006 23:18:43 +0000\" \"Mon, 3 Jul 2006 23:18:44 +0000\" \"Mon, 3 Jul 2006 23:18:45 +0000\"\n+'\n+\n+test_expect_success 'Check format \"raw\" date fields output' '\n+\ttest_date raw \"1151968723 +0200\" \"1151968724 +0200\" \"1151968725 +0200\"\n+'\n+\n+test_expect_success 'Check format \"raw-local\" date fields output' '\n+\ttest_date raw-local \"1151968723 +0000\" \"1151968724 +0000\" \"1151968725 +0000\"\n+'\n+\n+test_expect_success 'Check format of strftime date fields' '\n+\techo \"my date is 2006-07-04\" >expected &&\n+\t${git_for_each_ref} \\\n+\t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n+\t  refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check format of strftime-local date fields' '\n+\techo \"my date is 2006-07-03\" >expected &&\n+\t${git_for_each_ref} \\\n+\t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n+\t  refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'exercise strftime with odd fields' '\n+\techo >expected &&\n+\t${git_for_each_ref} --format=\"%(authordate:format:)\" refs/heads >actual &&\n+\ttest_cmp expected actual &&\n+\tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n+\techo $long >expected &&\n+\t${git_for_each_ref} --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/heads/main\n+refs/remotes/origin/main\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'Verify ascending sort' '\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=refname >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/remotes/origin/main\n+refs/heads/main\n+EOF\n+\n+test_expect_success 'Verify descending sort' '\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=-refname >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Give help even with invalid sort atoms' '\n+\ttest_expect_code 129 ${git_for_each_ref} --sort=bogus -h >actual 2>&1 &&\n+\tgrep \"^usage: ${git_for_each_ref}\" actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/tags/testtag-2\n+EOF\n+\n+test_expect_success 'exercise patterns with prefixes' '\n+\tgit tag testtag-2 &&\n+\ttest_when_finished \"git tag -d testtag-2\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/testtag\n+refs/tags/testtag-2\n+EOF\n+\n+test_expect_success 'exercise glob patterns with prefixes' '\n+\tgit tag testtag-2 &&\n+\ttest_when_finished \"git tag -d testtag-2\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/bar\n+refs/tags/baz\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'exercise patterns with prefix exclusions' '\n+\tfor tag in foo/one foo/two foo/three bar baz\n+\tdo\n+\t\tgit tag \"$tag\" || return 1\n+\tdone &&\n+\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/tags/bar\n+refs/tags/baz\n+refs/tags/foo/one\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'exercise patterns with pattern exclusions' '\n+\tfor tag in foo/one foo/two foo/three bar baz\n+\tdo\n+\t\tgit tag \"$tag\" || return 1\n+\tdone &&\n+\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+'refs/heads/main'\n+'refs/remotes/origin/main'\n+'refs/tags/testtag'\n+EOF\n+\n+test_expect_success 'Quoting style: shell' '\n+\t${git_for_each_ref} --shell --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Quoting style: perl' '\n+\t${git_for_each_ref} --perl --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Quoting style: python' '\n+\t${git_for_each_ref} --python --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+\"refs/heads/main\"\n+\"refs/remotes/origin/main\"\n+\"refs/tags/testtag\"\n+EOF\n+\n+test_expect_success 'Quoting style: tcl' '\n+\t${git_for_each_ref} --tcl --format=\"%(refname)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n+\ttest_expect_success \"more than one quoting style: $i\" \"\n+\t\ttest_must_fail ${git_for_each_ref} $i 2>err &&\n+\t\tgrep '^error: more than one quoting style' err\n+\t\"\n+done\n+\n+test_expect_success 'setup for upstream:track[short]' '\n+\ttest_commit two\n+'\n+\n+test_atom head upstream:track '[ahead 1]'\n+test_atom head upstream:trackshort '>'\n+test_atom head upstream:track,nobracket 'ahead 1'\n+test_atom head upstream:nobracket,track 'ahead 1'\n+\n+test_expect_success 'setup for push:track[short]' '\n+\ttest_commit third &&\n+\tgit update-ref refs/remotes/myfork/main main &&\n+\tgit reset main~1\n+'\n+\n+test_atom head push:track '[behind 1]'\n+test_atom head push:trackshort '<'\n+\n+test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:track)\" 2>/dev/null &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:trackshort)\" 2>/dev/null\n+'\n+\n+test_expect_success 'Check that :track[short] works when upstream is invalid' '\n+\tcat >expected <<-\\EOF &&\n+\t[gone]\n+\n+\tEOF\n+\ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n+\tgit config branch.main.merge refs/heads/does-not-exist &&\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n+\t\trefs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check for invalid refname format' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:INVALID)\"\n+'\n+\n+test_expect_success 'set up color tests' '\n+\tcat >expected.color <<-EOF &&\n+\t$(git rev-parse --short refs/heads/main) <GREEN>main<RESET>\n+\t$(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>\n+\t$(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>\n+\t$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>\n+\t$(git rev-parse --short refs/tags/third) <GREEN>third<RESET>\n+\t$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>\n+\tEOF\n+\tsed \"s/<[^>]*>//g\" <expected.color >expected.bare &&\n+\tcolor_format=\"%(objectname:short) %(color:green)%(refname:short)\"\n+'\n+\n+test_expect_success TTY '%(color) shows color with a tty' '\n+\ttest_terminal ${git_for_each_ref} --format=\"$color_format\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\ttest_cmp expected.color actual\n+'\n+\n+test_expect_success '%(color) does not show color without tty' '\n+\tTERM=vt100 ${git_for_each_ref} --format=\"$color_format\" >actual &&\n+\ttest_cmp expected.bare actual\n+'\n+\n+test_expect_success '--color can override tty check' '\n+\t${git_for_each_ref} --color --format=\"$color_format\" >actual.raw &&\n+\ttest_decode_color <actual.raw >actual &&\n+\ttest_cmp expected.color actual\n+'\n+\n+test_expect_success 'color.ui=always does not override tty check' '\n+\tgit -c color.ui=always ${git_for_each_ref#git} --format=\"$color_format\" >actual &&\n+\ttest_cmp expected.bare actual\n+'\n+\n+test_expect_success 'setup for describe atom tests' '\n+\tgit init -b master describe-repo &&\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\ttest_commit --no-tag one &&\n+\t\tgit tag tagone &&\n+\n+\t\ttest_commit --no-tag two &&\n+\t\tgit tag -a -m \"tag two\" tagtwo\n+\t)\n+'\n+\n+test_expect_success 'describe atom vs git describe' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t${git_for_each_ref} --format=\"%(objectname)\" \\\n+\t\t\trefs/tags/ >obj &&\n+\t\twhile read hash\n+\t\tdo\n+\t\t\tif desc=$(git describe $hash)\n+\t\t\tthen\n+\t\t\t\t: >expect-contains-good\n+\t\t\telse\n+\t\t\t\t: >expect-contains-bad\n+\t\t\tfi &&\n+\t\t\techo \"$hash $desc\" || return 1\n+\t\tdone <obj >expect &&\n+\t\ttest_path_exists expect-contains-good &&\n+\t\ttest_path_exists expect-contains-bad &&\n+\n+\t\t${git_for_each_ref} --format=\"%(objectname) %(describe)\" \\\n+\t\t\trefs/tags/ >actual 2>err &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest_must_be_empty err\n+\t)\n+'\n+\n+test_expect_success 'describe:tags vs describe --tags' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit describe --tags >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:tags)\" \\\n+\t\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t# Case 1: We have commits between HEAD and the most\n+\t\t#\t  recent tag reachable from it\n+\t\ttest_commit --no-tag file &&\n+\t\tgit describe --abbrev=14 >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:abbrev=14)\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# Make sure the hash used is at least 14 digits long\n+\t\tsed -e \"s/^.*-g\\([0-9a-f]*\\)$/\\1/\" <actual >hexpart &&\n+\t\ttest 15 -le $(wc -c <hexpart) &&\n+\n+\t\t# Case 2: We have a tag at HEAD, describe directly gives\n+\t\t#\t  the name of the tag\n+\t\tgit tag -a -m tagged tagname &&\n+\t\tgit describe --abbrev=14 >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:abbrev=14)\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest tagname = $(cat actual)\n+\t)\n+'\n+\n+test_expect_success 'describe:match=... vs describe --match ...' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit tag -a -m \"tag foo\" tag-foo &&\n+\t\tgit describe --match \"*-foo\" >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:match=\"*-foo\")\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tgit tag -a -m \"tag bar\" tag-bar &&\n+\t\tgit describe --exclude \"*-bar\" >expect &&\n+\t\t${git_for_each_ref} --format=\"%(describe:exclude=\"*-bar\")\" \\\n+\t\t\trefs/heads/master >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'deref with describe atom' '\n+\t(\n+\t\tcd describe-repo &&\n+\t\tcat >expect <<-\\EOF &&\n+\n+\t\ttagname\n+\t\ttagname\n+\t\ttagname\n+\n+\t\ttagtwo\n+\t\tEOF\n+\t\t${git_for_each_ref} --format=\"%(*describe)\" >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'err on bad describe atom arg' '\n+\t(\n+\t\tcd describe-repo &&\n+\n+\t\t# The bad arg is the only arg passed to describe atom\n+\t\tcat >expect <<-\\EOF &&\n+\t\tfatal: unrecognized %(describe) argument: baz\n+\t\tEOF\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%(describe:baz)\" \\\n+\t\t\trefs/heads/master 2>actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# The bad arg is in the middle of the option string\n+\t\t# passed to the describe atom\n+\t\tcat >expect <<-\\EOF &&\n+\t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n+\t\tEOF\n+\t\ttest_must_fail ${git_for_each_ref} \\\n+\t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n+\t\t\tref/heads/master 2>actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+cat >expected <<\\EOF\n+heads/main\n+tags/main\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs (strict)' '\n+\tgit config --bool core.warnambiguousrefs true &&\n+\tgit checkout -b newtag &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Branch\" &&\n+\tsetdate_and_increment &&\n+\tgit tag -m \"Tagging at $datestamp\" main &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/main\n+main\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs (loose)' '\n+\tgit config --bool core.warnambiguousrefs false &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/ambiguous\n+ambiguous\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n+\tgit checkout main &&\n+\tgit tag ambiguous testtag^0 &&\n+\tgit branch ambiguous testtag^0 &&\n+\t${git_for_each_ref} --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'create tag without tagger' '\n+\tgit tag -a -m \"Broken tag\" taggerless &&\n+\tgit tag -f taggerless $(git cat-file tag taggerless |\n+\t\tsed -e \"/^tagger /d\" |\n+\t\tgit hash-object --literally --stdin -w -t tag)\n+'\n+\n+test_atom refs/tags/taggerless type 'commit'\n+test_atom refs/tags/taggerless tag 'taggerless'\n+test_atom refs/tags/taggerless tagger ''\n+test_atom refs/tags/taggerless taggername ''\n+test_atom refs/tags/taggerless taggeremail ''\n+test_atom refs/tags/taggerless taggeremail:trim ''\n+test_atom refs/tags/taggerless taggeremail:localpart ''\n+test_atom refs/tags/taggerless taggerdate ''\n+test_atom refs/tags/taggerless committer ''\n+test_atom refs/tags/taggerless committername ''\n+test_atom refs/tags/taggerless committeremail ''\n+test_atom refs/tags/taggerless committeremail:trim ''\n+test_atom refs/tags/taggerless committeremail:localpart ''\n+test_atom refs/tags/taggerless committerdate ''\n+test_atom refs/tags/taggerless subject 'Broken tag'\n+\n+test_expect_success 'an unusual tag with an incomplete line' '\n+\n+\tgit tag -m \"bogo\" bogo &&\n+\tbogo=$(git cat-file tag bogo) &&\n+\tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n+\tgit tag -f bogo \"$bogo\" &&\n+\t${git_for_each_ref} --format \"%(body)\" refs/tags/bogo\n+\n+'\n+\n+test_expect_success 'create tag with subject and body content' '\n+\tcat >>msg <<-\\EOF &&\n+\t\tthe subject line\n+\n+\t\tfirst body line\n+\t\tsecond body line\n+\tEOF\n+\tgit tag -F msg subject-body\n+'\n+test_atom refs/tags/subject-body subject 'the subject line'\n+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n+test_atom refs/tags/subject-body body 'first body line\n+second body line\n+'\n+test_atom refs/tags/subject-body contents 'the subject line\n+\n+first body line\n+second body line\n+'\n+\n+test_expect_success 'create tag with multiline subject' '\n+\tcat >msg <<-\\EOF &&\n+\t\tfirst subject line\n+\t\tsecond subject line\n+\n+\t\tfirst body line\n+\t\tsecond body line\n+\tEOF\n+\tgit tag -F msg multiline\n+'\n+test_atom refs/tags/multiline subject 'first subject line second subject line'\n+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n+test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n+test_atom refs/tags/multiline body 'first body line\n+second body line\n+'\n+test_atom refs/tags/multiline contents:body 'first body line\n+second body line\n+'\n+test_atom refs/tags/multiline contents:signature ''\n+test_atom refs/tags/multiline contents 'first subject line\n+second subject line\n+\n+first body line\n+second body line\n+'\n+\n+test_expect_success GPG 'create signed tags' '\n+\tgit tag -s -m \"\" signed-empty &&\n+\tgit tag -s -m \"subject line\" signed-short &&\n+\tcat >msg <<-\\EOF &&\n+\tsubject line\n+\n+\tbody contents\n+\tEOF\n+\tgit tag -s -F msg signed-long\n+'\n+\n+sig='-----BEGIN PGP SIGNATURE-----\n+-----END PGP SIGNATURE-----\n+'\n+\n+PREREQ=GPG\n+test_atom refs/tags/signed-empty subject ''\n+test_atom refs/tags/signed-empty subject:sanitize ''\n+test_atom refs/tags/signed-empty contents:subject ''\n+test_atom refs/tags/signed-empty body \"$sig\"\n+test_atom refs/tags/signed-empty contents:body ''\n+test_atom refs/tags/signed-empty contents:signature \"$sig\"\n+test_atom refs/tags/signed-empty contents \"$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n+\tgit cat-file tag refs/tags/signed-empty >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_atom refs/tags/signed-short subject 'subject line'\n+test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n+test_atom refs/tags/signed-short contents:subject 'subject line'\n+test_atom refs/tags/signed-short body \"$sig\"\n+test_atom refs/tags/signed-short contents:body ''\n+test_atom refs/tags/signed-short contents:signature \"$sig\"\n+test_atom refs/tags/signed-short contents \"subject line\n+$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n+\tgit cat-file tag refs/tags/signed-short >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-short >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_atom refs/tags/signed-long subject 'subject line'\n+test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n+test_atom refs/tags/signed-long contents:subject 'subject line'\n+test_atom refs/tags/signed-long body \"body contents\n+$sig\"\n+test_atom refs/tags/signed-long contents:body 'body contents\n+'\n+test_atom refs/tags/signed-long contents:signature \"$sig\"\n+test_atom refs/tags/signed-long contents \"subject line\n+\n+body contents\n+$sig\"\n+\n+test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n+\tgit cat-file tag refs/tags/signed-long >expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/tags/signed-long >actual &&\n+\tsanitize_pgp <expected >expected.clean &&\n+\techo >>expected.clean &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\ttest_cmp expected.clean actual.clean\n+'\n+\n+test_expect_success 'set up refs pointing to tree and blob' '\n+\tgit update-ref refs/mytrees/first refs/heads/main^{tree} &&\n+\tgit update-ref refs/myblobs/first refs/heads/main:one\n+'\n+\n+test_atom refs/mytrees/first subject \"\"\n+test_atom refs/mytrees/first contents:subject \"\"\n+test_atom refs/mytrees/first body \"\"\n+test_atom refs/mytrees/first contents:body \"\"\n+test_atom refs/mytrees/first contents:signature \"\"\n+test_atom refs/mytrees/first contents \"\"\n+\n+test_expect_success 'basic atom: refs/mytrees/first raw' '\n+\tgit cat-file tree refs/mytrees/first >expected &&\n+\techo >>expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file -s refs/mytrees/first >expected &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_atom refs/myblobs/first subject \"\"\n+test_atom refs/myblobs/first contents:subject \"\"\n+test_atom refs/myblobs/first body \"\"\n+test_atom refs/myblobs/first contents:body \"\"\n+test_atom refs/myblobs/first contents:signature \"\"\n+test_atom refs/myblobs/first contents \"\"\n+\n+test_expect_success 'basic atom: refs/myblobs/first raw' '\n+\tgit cat-file blob refs/myblobs/first >expected &&\n+\techo >>expected &&\n+\t${git_for_each_ref} --format=\"%(raw)\" refs/myblobs/first >actual &&\n+\ttest_cmp expected actual &&\n+\tgit cat-file -s refs/myblobs/first >expected &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'set up refs pointing to binary blob' '\n+\tprintf \"a\\0b\\0c\" >blob1 &&\n+\tprintf \"a\\0c\\0b\" >blob2 &&\n+\tprintf \"\\0a\\0b\\0c\" >blob3 &&\n+\tprintf \"abc\" >blob4 &&\n+\tprintf \"\\0 \\0 \\0 \" >blob5 &&\n+\tprintf \"\\0 \\0a\\0 \" >blob6 &&\n+\tprintf \"  \" >blob7 &&\n+\t>blob8 &&\n+\tobj=$(git hash-object -w blob1) &&\n+\tgit update-ref refs/myblobs/blob1 \"$obj\" &&\n+\tobj=$(git hash-object -w blob2) &&\n+\tgit update-ref refs/myblobs/blob2 \"$obj\" &&\n+\tobj=$(git hash-object -w blob3) &&\n+\tgit update-ref refs/myblobs/blob3 \"$obj\" &&\n+\tobj=$(git hash-object -w blob4) &&\n+\tgit update-ref refs/myblobs/blob4 \"$obj\" &&\n+\tobj=$(git hash-object -w blob5) &&\n+\tgit update-ref refs/myblobs/blob5 \"$obj\" &&\n+\tobj=$(git hash-object -w blob6) &&\n+\tgit update-ref refs/myblobs/blob6 \"$obj\" &&\n+\tobj=$(git hash-object -w blob7) &&\n+\tgit update-ref refs/myblobs/blob7 \"$obj\" &&\n+\tobj=$(git hash-object -w blob8) &&\n+\tgit update-ref refs/myblobs/blob8 \"$obj\"\n+'\n+\n+test_expect_success 'Verify sorts with raw' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob8\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/blob3\n+\trefs/myblobs/blob7\n+\trefs/mytrees/first\n+\trefs/myblobs/first\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob4\n+\trefs/heads/main\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=raw \\\n+\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Verify sorts with raw:size' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob8\n+\trefs/myblobs/blob7\n+\trefs/myblobs/blob4\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob3\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/first\n+\trefs/mytrees/first\n+\trefs/heads/main\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" --sort=raw:size \\\n+\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'validate raw atom with %(if:equals)' '\n+\tcat >expected <<-EOF &&\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\trefs/myblobs/blob4\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tnot equals\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n+\t\trefs/myblobs/ refs/heads/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'validate raw atom with %(if:notequals)' '\n+\tcat >expected <<-EOF &&\n+\trefs/heads/ambiguous\n+\trefs/heads/main\n+\trefs/heads/newtag\n+\trefs/myblobs/blob1\n+\trefs/myblobs/blob2\n+\trefs/myblobs/blob3\n+\tequals\n+\trefs/myblobs/blob5\n+\trefs/myblobs/blob6\n+\trefs/myblobs/blob7\n+\trefs/myblobs/blob8\n+\trefs/myblobs/first\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n+\t\trefs/myblobs/ refs/heads/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'empty raw refs with %(if)' '\n+\tcat >expected <<-EOF &&\n+\trefs/myblobs/blob1 not empty\n+\trefs/myblobs/blob2 not empty\n+\trefs/myblobs/blob3 not empty\n+\trefs/myblobs/blob4 not empty\n+\trefs/myblobs/blob5 not empty\n+\trefs/myblobs/blob6 not empty\n+\trefs/myblobs/blob7 empty\n+\trefs/myblobs/blob8 empty\n+\trefs/myblobs/first not empty\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n+\t\trefs/myblobs/ >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '%(raw) with --python must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --python\n+'\n+\n+test_expect_success '%(raw) with --tcl must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --tcl\n+'\n+\n+test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n+\tcmp blob1 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n+\tcmp blob3 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n+\tcmp blob8 actual &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n+\tcmp one actual &&\n+\tgit cat-file tree refs/mytrees/first > expected &&\n+\t${git_for_each_ref} --format=\"\\$name= %(raw);\n+print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n+\tcmp expected actual\n+'\n+\n+test_expect_success '%(raw) with --shell must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --shell\n+'\n+\n+test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(raw)\" --sort=raw --shell\n+'\n+\n+test_expect_success '%(raw:size) with --shell' '\n+\t${git_for_each_ref} --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n+\t${git_for_each_ref} --format=\"%(raw:size)\" --shell >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --format compare with cat-file --batch\" '\n+\tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n+\t${git_for_each_ref} --format=\"%(objectname) %(objecttype) %(objectsize)\n+%(raw)\" refs/mytrees/first >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'verify sorts with contents:size' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/main\n+\trefs/heads/newtag\n+\trefs/heads/ambiguous\n+\tEOF\n+\t${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\t--sort=contents:size refs/heads/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'set up multiple-sort tags' '\n+\tfor when in 100000 200000\n+\tdo\n+\t\tfor email in user1 user2\n+\t\tdo\n+\t\t\tfor ref in ref1 ref2\n+\t\t\tdo\n+\t\t\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n+\t\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n+\t\t\t\tgit tag -m \"tag $ref-$when-$email\" \\\n+\t\t\t\tmulti-$ref-$when-$email || return 1\n+\t\t\tdone\n+\t\tdone\n+\tdone\n+'\n+\n+test_expect_success 'Verify sort with multiple keys' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=-refname \\\n+\t\t--sort=taggeremail \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'equivalent sorts fall back on refname' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--no-sort cancels the previous sort keys' '\n+\tcat >expected <<-\\EOF &&\n+\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n+\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n+\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n+\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n+\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n+\tEOF\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n+\t\t--sort=-refname \\\n+\t\t--sort=taggeremail \\\n+\t\t--no-sort \\\n+\t\t--sort=taggerdate \\\n+\t\t\"refs/tags/multi-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n+\tcat >expected <<-\\EOF &&\n+\trefs/tags/multi-ref1-100000-user1\n+\trefs/tags/multi-ref1-100000-user2\n+\trefs/tags/multi-ref1-200000-user1\n+\trefs/tags/multi-ref1-200000-user2\n+\trefs/tags/multi-ref2-100000-user1\n+\trefs/tags/multi-ref2-100000-user2\n+\trefs/tags/multi-ref2-200000-user1\n+\trefs/tags/multi-ref2-200000-user2\n+\tEOF\n+\n+\t# Sort the results with `sort` for a consistent comparison against\n+\t# expected\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(refname)\" \\\n+\t\t--no-sort \\\n+\t\t\"refs/tags/multi-*\" | sort >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'set up custom date sorting' '\n+\t# Dates:\n+\t# - Wed Feb 07 2024 21:34:20 +0000\n+\t# - Tue Dec 14 1999 00:05:22 +0000\n+\t# - Fri Jun 04 2021 11:26:51 +0000\n+\t# - Mon Jan 22 2007 16:44:01 GMT+0000\n+\ti=1 &&\n+\tfor when in 1707341660 945129922 1622806011 1169484241\n+\tdo\n+\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n+\t\tGIT_COMMITTER_EMAIL=\"user@example.com\" \\\n+\t\tgit tag -m \"tag $when\" custom-dates-$i &&\n+\t\ti=$(($i+1)) || return 1\n+\tdone\n+'\n+\n+test_expect_success 'sort by date defaults to full timestamp' '\n+\tcat >expected <<-\\EOF &&\n+\t945129922 refs/tags/custom-dates-2\n+\t1169484241 refs/tags/custom-dates-4\n+\t1622806011 refs/tags/custom-dates-3\n+\t1707341660 refs/tags/custom-dates-1\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(creatordate:unix) %(refname)\" \\\n+\t\t--sort=creatordate \\\n+\t\t\"refs/tags/custom-dates-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'sort by custom date format' '\n+\tcat >expected <<-\\EOF &&\n+\t00:05:22 refs/tags/custom-dates-2\n+\t11:26:51 refs/tags/custom-dates-3\n+\t16:44:01 refs/tags/custom-dates-4\n+\t21:34:20 refs/tags/custom-dates-1\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n+\t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n+\t\t\"refs/tags/custom-dates-*\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n+\ttest_when_finished \"git checkout main\" &&\n+\t${git_for_each_ref} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\tsed -e \"s/^\\* /  /\" actual >expect &&\n+\tgit checkout --orphan orphaned-branch &&\n+\t${git_for_each_ref} --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+cat >trailers <<EOF\n+Reviewed-by: A U Thor <author@example.com>\n+Signed-off-by: A U Thor <author@example.com>\n+[ v2 updated patch description ]\n+Acked-by: A U Thor\n+  <author@example.com>\n+EOF\n+\n+unfold () {\n+\tperl -0pe 's/\\n\\s+/ /g'\n+}\n+\n+test_expect_success 'set up trailers for next test' '\n+\techo \"Some contents\" > two &&\n+\tgit add two &&\n+\tgit commit -F - <<-EOF\n+\ttrailers: this commit message has trailers\n+\n+\tSome message contents\n+\n+\t$(cat trailers)\n+\tEOF\n+'\n+\n+test_trailer_option () {\n+\tif test \"$#\" -eq 3\n+\tthen\n+\t\tprereq=\"$1\"\n+\t\tshift\n+\tfi &&\n+\ttitle=$1 option=$2\n+\tcat >expect\n+\ttest_expect_success $prereq \"$title\" '\n+\t\t${git_for_each_ref} --format=\"%($option)\" refs/heads/main >actual &&\n+\t\ttest_cmp expect actual &&\n+\t\t${git_for_each_ref} --format=\"%(contents:$option)\" refs/heads/main >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \\\n+\t'trailers:unfold' <<-EOF\n+\t$(unfold <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only) shows only \"key: value\" trailers' \\\n+\t'trailers:only' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=no,only=true) shows only \"key: value\" trailers' \\\n+\t'trailers:only=no,only=true' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=yes) shows only \"key: value\" trailers' \\\n+\t'trailers:only=yes' <<-EOF\n+\t$(grep -v patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:only=no) shows all trailers' \\\n+\t'trailers:only=no' <<-EOF\n+\t$(cat trailers)\n+\n+\tEOF\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \\\n+\t'trailers:only,unfold' <<-EOF\n+\t$(grep -v patch.description <trailers | unfold)\n+\n+\tEOF\n+\n+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \\\n+\t'trailers:unfold,only' <<-EOF\n+\t$(grep -v patch.description <trailers | unfold)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) shows that trailer' \\\n+\t'trailers:key=Signed-off-by' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) is case insensitive' \\\n+\t'trailers:key=SiGned-oFf-bY' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo:) trailing colon also works' \\\n+\t'trailers:key=Signed-off-by:' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) multiple keys' \\\n+\t'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF\n+\tReviewed-by: A U Thor <author@example.com>\n+\tSigned-off-by: A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=nonexistent) becomes empty' \\\n+\t'trailers:key=Shined-off-by:' <<-EOF\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \\\n+\t'trailers:key=Acked-by' <<-EOF\n+\t$(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \\\n+\t'trailers:key=Signed-Off-by,unfold' <<-EOF\n+\t$(unfold <trailers | grep Signed-off-by)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \\\n+\t'trailers:key=Signed-off-by,only=no' <<-EOF\n+\tSigned-off-by: A U Thor <author@example.com>\n+\t$(grep patch.description <trailers)\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \\\n+\t'trailers:key=Signed-off-by,valueonly' <<-EOF\n+\tA U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:separator) changes separator' \\\n+\t'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>\n+\tEOF\n+\n+test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \\\n+\t'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by,A U Thor <author@example.com>\n+\tSigned-off-by,A U Thor <author@example.com>\n+\n+\tEOF\n+\n+test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \\\n+\t'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n+\tReviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>\n+\tEOF\n+\n+test_expect_success 'multiple %(trailers) use their own options' '\n+\tgit tag -F - tag-with-trailers <<-\\EOF &&\n+\tbody\n+\n+\tone: foo\n+\tone: bar\n+\ttwo: baz\n+\ttwo: qux\n+\tEOF\n+\tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n+\tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n+\t${git_for_each_ref} --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n+\tcat >expect <<-\\EOF &&\n+\toneWfooXoneWbar\n+\ttwoYbazZtwoYqux\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_failing_trailer_option () {\n+\ttitle=$1 option=$2\n+\tcat >expect\n+\ttest_expect_success \"$title\" '\n+\t\t# error message cannot be checked under i18n\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%($option)\" refs/heads/main 2>actual &&\n+\t\ttest_cmp expect actual &&\n+\t\ttest_must_fail ${git_for_each_ref} --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \\\n+\t'trailers:unsupported' <<-\\EOF\n+\tfatal: unknown %(trailers) argument: unsupported\n+\tEOF\n+\n+test_failing_trailer_option '%(trailers:key) without value is error' \\\n+\t'trailers:key' <<-\\EOF\n+\tfatal: expected %(trailers:key=<value>)\n+\tEOF\n+\n+test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '\n+\tcat >expect <<-EOF &&\n+\tfatal: unrecognized %(contents) argument: trailersonly\n+\tEOF\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(contents:trailersonly)\" 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'basic atom: head contents:trailers' '\n+\t${git_for_each_ref} --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n+\tsanitize_pgp <actual >actual.clean &&\n+\t# ${git_for_each_ref} ends with a blank line\n+\tcat >expect <<-EOF &&\n+\t$(cat trailers)\n+\n+\tEOF\n+\ttest_cmp expect actual.clean\n+'\n+\n+test_expect_success 'basic atom: rest must fail' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(rest)\" refs/heads/main\n+'\n+\n+test_expect_success 'HEAD atom does not take arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(HEAD:foo)\" 2>err &&\n+\techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'subject atom rejects unknown arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(subject:foo)\" 2>err &&\n+\techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'refname atom rejects unknown arguments' '\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname:foo)\" 2>err &&\n+\techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n+\ttest_cmp expect err\n+'\n+\n+test_expect_success 'trailer parsing not fooled by --- line' '\n+\tgit commit --allow-empty -F - <<-\\EOF &&\n+\tthis is the subject\n+\n+\tThis is the body. The message has a \"---\" line which would confuse a\n+\tmessage+patch parser. But here we know we have only a commit message,\n+\tso we get it right.\n+\n+\ttrailer: wrong\n+\t---\n+\tThis is more body.\n+\n+\ttrailer: right\n+\tEOF\n+\n+\t{\n+\t\techo \"trailer: right\" &&\n+\t\techo\n+\t} >expect &&\n+\t${git_for_each_ref} --format=\"%(trailers)\" refs/heads/main >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'Add symbolic ref for the following tests' '\n+\tgit symbolic-ref refs/heads/sym refs/heads/main\n+'\n+\n+cat >expected <<EOF\n+refs/heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref) atom' '\n+\t${git_for_each_ref} --format=\"%(symref)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:short) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:short)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+main\n+heads/main\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual &&\n+\n+\t${git_for_each_ref} --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+refs\n+refs/heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n+\t${git_for_each_ref} --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n+\t${git_for_each_ref} --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success ':remotename and :remoteref' '\n+\tgit init remote-tests &&\n+\t(\n+\t\tcd remote-tests &&\n+\t\ttest_commit initial &&\n+\t\tgit branch -M main &&\n+\t\tgit remote add from fifth.coffee:blub &&\n+\t\tgit config branch.main.remote from &&\n+\t\tgit config branch.main.merge refs/heads/stable &&\n+\t\tgit remote add to southridge.audio:repo &&\n+\t\tgit config remote.to.push \"refs/heads/*:refs/heads/pushed/*\" &&\n+\t\tgit config branch.main.pushRemote to &&\n+\t\tfor pair in \"%(upstream)=refs/remotes/from/stable\" \\\n+\t\t\t\"%(upstream:remotename)=from\" \\\n+\t\t\t\"%(upstream:remoteref)=refs/heads/stable\" \\\n+\t\t\t\"%(push)=refs/remotes/to/pushed/main\" \\\n+\t\t\t\"%(push:remotename)=to\" \\\n+\t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n+\t\tdo\n+\t\t\techo \"${pair#*=}\" >expect &&\n+\t\t\t${git_for_each_ref} --format=\"${pair%=*}\" \\\n+\t\t\t\trefs/heads/main >actual &&\n+\t\t\ttest_cmp expect actual || exit 1\n+\t\tdone &&\n+\t\tgit branch push-simple &&\n+\t\tgit config branch.push-simple.pushRemote from &&\n+\t\tactual=\"$(${git_for_each_ref} \\\n+\t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n+\t\t\trefs/heads/push-simple)\" &&\n+\t\ttest from, = \"$actual\"\n+\t)\n+'\n+\n+test_expect_success \"${git_for_each_ref} --ignore-case ignores case\" '\n+\t${git_for_each_ref} --format=\"%(refname)\" refs/heads/MAIN >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\techo refs/heads/main >expect &&\n+\t${git_for_each_ref} --format=\"%(refname)\" --ignore-case \\\n+\t\trefs/heads/MAIN >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --omit-empty works\" '\n+\t${git_for_each_ref} --format=\"%(refname)\" >actual &&\n+\ttest_line_count -gt 1 actual &&\n+\t${git_for_each_ref} --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n+\techo refs/heads/main >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --ignore-case works on multiple sort keys\" '\n+\t# name refs numerically to avoid case-insensitive filesystem conflicts\n+\tnr=0 &&\n+\tfor email in a A b B\n+\tdo\n+\t\tfor subject in a A b B\n+\t\tdo\n+\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n+\t\t\tgit tag -m \"tag $subject\" icase-$(printf %02d $nr) &&\n+\t\t\tnr=$((nr+1))||\n+\t\t\treturn 1\n+\t\tdone\n+\tdone &&\n+\t${git_for_each_ref} --ignore-case \\\n+\t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n+\t\t--sort=refname \\\n+\t\t--sort=subject \\\n+\t\t--sort=taggeremail \\\n+\t\trefs/tags/icase-* >actual &&\n+\tcat >expect <<-\\EOF &&\n+\t<a@example.com> tag a refs/tags/icase-00\n+\t<a@example.com> tag A refs/tags/icase-01\n+\t<A@example.com> tag a refs/tags/icase-04\n+\t<A@example.com> tag A refs/tags/icase-05\n+\t<a@example.com> tag b refs/tags/icase-02\n+\t<a@example.com> tag B refs/tags/icase-03\n+\t<A@example.com> tag b refs/tags/icase-06\n+\t<A@example.com> tag B refs/tags/icase-07\n+\t<b@example.com> tag a refs/tags/icase-08\n+\t<b@example.com> tag A refs/tags/icase-09\n+\t<B@example.com> tag a refs/tags/icase-12\n+\t<B@example.com> tag A refs/tags/icase-13\n+\t<b@example.com> tag b refs/tags/icase-10\n+\t<b@example.com> tag B refs/tags/icase-11\n+\t<B@example.com> tag b refs/tags/icase-14\n+\t<B@example.com> tag B refs/tags/icase-15\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} reports broken tags\" '\n+\tgit tag -m \"good tag\" broken-tag-good HEAD &&\n+\tgit cat-file tag broken-tag-good >good &&\n+\tsed s/commit/blob/ <good >bad &&\n+\tbad=$(git hash-object -w -t tag bad) &&\n+\tgit update-ref refs/tags/broken-tag-bad $bad &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(*objectname)\" \\\n+\t\trefs/tags/broken-tag-*\n+'\n+\n+test_expect_success 'set up tag with signature and no blank lines' '\n+\tgit tag -F - fake-sig-no-blanks <<-\\EOF\n+\tthis is the subject\n+\t-----BEGIN PGP SIGNATURE-----\n+\tnot a real signature, but we just care about the\n+\tsubject/body parsing. It is important here that\n+\tthere are no blank lines in the signature.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+'\n+\n+test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'\n+test_atom refs/tags/fake-sig-no-blanks contents:body ''\n+test_atom refs/tags/fake-sig-no-blanks contents:signature \"$sig\"\n+\n+test_expect_success 'set up tag with CRLF signature' '\n+\tappend_cr <<-\\EOF |\n+\tthis is the subject\n+\t-----BEGIN PGP SIGNATURE-----\n+\n+\tnot a real signature, but we just care about\n+\tthe subject/body parsing. It is important here\n+\tthat there is a blank line separating this\n+\tfrom the signature header.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+\tgit tag -F - --cleanup=verbatim fake-sig-crlf\n+'\n+\n+test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'\n+test_atom refs/tags/fake-sig-crlf contents:body ''\n+\n+# CRLF is retained in the signature, so we have to pass our expected value\n+# through append_cr. But test_atom requires a shell string, which means command\n+# substitution, and the shell will strip trailing newlines from the output of\n+# the substitution. Hack around it by adding and then removing a dummy line.\n+sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n+sig_crlf=${sig_crlf%dummy}\n+test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n+\n+test_expect_success 'set up tag with signature and trailers' '\n+\tgit tag -F - fake-sig-trailer <<-\\EOF\n+\tthis is the subject\n+\n+\tthis is the body\n+\n+\tMy-Trailer: foo\n+\t-----BEGIN PGP SIGNATURE-----\n+\n+\tnot a real signature, but we just care about the\n+\tsubject/body/trailer parsing.\n+\t-----END PGP SIGNATURE-----\n+\tEOF\n+'\n+\n+# use \"separator=\" here to suppress the terminating newline\n+test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: empty\" '\n+\t>in &&\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\t${git_for_each_ref} --format=\"%(refname)\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: fails if extra args\" '\n+\t>in &&\n+\ttest_must_fail ${git_for_each_ref} --format=\"%(refname)\" \\\n+\t\t--stdin refs/heads/extra <in 2>err &&\n+\tgrep \"unknown arguments supplied with --stdin\" err\n+'\n+\n+test_expect_success \"${git_for_each_ref} --stdin: matches\" '\n+\tcat >in <<-EOF &&\n+\trefs/tags/multi*\n+\trefs/heads/amb*\n+\tEOF\n+\n+\tcat >expect <<-EOF &&\n+\trefs/heads/ambiguous\n+\trefs/tags/multi-ref1-100000-user1\n+\trefs/tags/multi-ref1-100000-user2\n+\trefs/tags/multi-ref1-200000-user1\n+\trefs/tags/multi-ref1-200000-user2\n+\trefs/tags/multi-ref2-100000-user1\n+\trefs/tags/multi-ref2-100000-user2\n+\trefs/tags/multi-ref2-200000-user1\n+\trefs/tags/multi-ref2-200000-user2\n+\trefs/tags/multiline\n+\tEOF\n+\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} with non-existing refs\" '\n+\tcat >in <<-EOF &&\n+\trefs/heads/this-ref-does-not-exist\n+\trefs/tags/bogus\n+\tEOF\n+\n+\t${git_for_each_ref} --format=\"%(refname)\" --stdin <in >actual &&\n+\ttest_must_be_empty actual &&\n+\n+\txargs ${git_for_each_ref} --format=\"%(refname)\" <in >actual &&\n+\ttest_must_be_empty actual\n+'\n+\n+test_expect_success \"${git_for_each_ref} with nested tags\" '\n+\tgit tag -am \"Normal tag\" nested/base HEAD &&\n+\tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n+\tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n+\n+\thead_oid=\"$(git rev-parse HEAD)\" &&\n+\tbase_tag_oid=\"$(git rev-parse refs/tags/nested/base)\" &&\n+\tnest1_tag_oid=\"$(git rev-parse refs/tags/nested/nest1)\" &&\n+\tnest2_tag_oid=\"$(git rev-parse refs/tags/nested/nest2)\" &&\n+\n+\tcat >expect <<-EOF &&\n+\trefs/tags/nested/base $base_tag_oid tag $head_oid commit\n+\trefs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit\n+\trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n+\tEOF\n+\n+\t${git_for_each_ref} \\\n+\t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n+\t\trefs/tags/nested/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'is-base atom with non-commits' '\n+\t${git_for_each_ref} --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n+\tgrep \"(HEAD) refs/heads/main\" out &&\n+\n+\ttest_line_count = 2 err &&\n+\tgrep \"error: object .* is a commit, not a blob\" err &&\n+\tgrep \"error: bad tag pointer to\" err\n+'\n+\n+GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n+\n+test_expect_success GPG 'setup for signature atom using gpg' '\n+\tgit checkout -b signed &&\n+\n+\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n+\n+\techo \"1\" >file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -S -m \"file: 1\" &&\n+\tgit tag first-signed &&\n+\n+\techo \"2\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -m \"file: 2\" &&\n+\tgit tag second-unsigned &&\n+\n+\tgit config commit.gpgSign 1 &&\n+\techo \"3\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n+\tgit tag third-unsigned &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n+\tgit tag third-signed &&\n+\n+\techo \"4\" >file &&\n+\ttest_tick &&\n+\tgit commit -a -SB7227189 -m \"file: 4\" &&\n+\tgit tag fourth-signed &&\n+\n+\techo \"5\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n+\tgit tag fifth-unsigned &&\n+\n+\techo \"6\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n+\n+\ttest_tick &&\n+\tgit rebase -f HEAD^^ &&\n+\tgit tag fifth-signed HEAD^ &&\n+\tgit tag sixth-signed &&\n+\n+\techo \"7\" >file &&\n+\ttest_tick &&\n+\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n+\tgit tag seventh-unsigned\n+'\n+\n+test_expect_success GPGSSH 'setup for signature atom using ssh' '\n+\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n+\n+\ttest_config gpg.format ssh &&\n+\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n+\techo \"8\" >file &&\n+\ttest_tick &&\n+\tgit add file &&\n+\tgit commit -S -m \"file: 8\" &&\n+\tgit tag eighth-signed-ssh\n+'\n+\n+test_expect_success GPG2 'bare signature atom' '\n+\tgit verify-commit first-signed 2>expect &&\n+\techo  >>expect &&\n+\t${git_for_each_ref} refs/tags/first-signed \\\n+\t\t--format=\"%(signature)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show good signature with custom format' '\n+\tgit verify-commit first-signed &&\n+\tcat >expect <<-\\EOF &&\n+\tG\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\t${git_for_each_ref} refs/tags/first-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n+\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n+\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n+\tcat >expect.tmpl <<-\\EOF &&\n+\tG\n+\tFINGERPRINT\n+\tprincipal with number 1\n+\tFINGERPRINT\n+\n+\tEOF\n+\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n+\t${git_for_each_ref} refs/tags/eighth-signed-ssh \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'signature atom with grade option and bad signature' '\n+\tgit cat-file commit third-signed >raw &&\n+\tsed -e \"s/^file: 3/file: 3 forged/\" raw >forged1 &&\n+\tFORGED1=$(git hash-object -w -t commit forged1) &&\n+\tgit update-ref refs/tags/third-signed \"$FORGED1\" &&\n+\ttest_must_fail git verify-commit \"$FORGED1\" &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tB\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\n+\n+\tEOF\n+\t${git_for_each_ref} refs/tags/third-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tU\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\t${git_for_each_ref} refs/tags/fourth-signed \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with undefined trust level' '\n+\tcat >expect <<-\\EOF &&\n+\tundefined\n+\t65A0EEA02E30CAD7\n+\tEris Discordia <discord@example.net>\n+\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n+\tD4BE22311AD3131E5EDA29A461092E85B7227189\n+\tEOF\n+\t${git_for_each_ref} refs/tags/fourth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n+\tcat >expect <<-\\EOF &&\n+\tultimate\n+\t13B6F51ECDDE430D\n+\tC O Mitter <committer@example.com>\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\t73D758744BE721698EC54E8713B6F51ECDDE430D\n+\tEOF\n+\t${git_for_each_ref} refs/tags/sixth-signed \\\n+\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show unknown signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tE\n+\t13B6F51ECDDE430D\n+\n+\n+\n+\tEOF\n+\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" ${git_for_each_ref} \\\n+\t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success GPG 'show lack of signature with custom format' '\n+\tcat >expect <<-\\EOF &&\n+\tN\n+\n+\n+\n+\n+\tEOF\n+\t${git_for_each_ref} refs/tags/seventh-unsigned \\\n+\t\t--format=\"$GRADE_FORMAT\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex ce9af79ab1..1d9809114d 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -6,2150 +6,14 @@\n test_description='for-each-ref test'\n \n . ./test-lib.sh\n-GNUPGHOME_NOT_USED=$GNUPGHOME\n-. \"$TEST_DIRECTORY\"/lib-gpg.sh\n-. \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n-# Mon Jul 3 23:18:43 2006 +0000\n-datestamp=1151968723\n-setdate_and_increment () {\n-    GIT_COMMITTER_DATE=\"$datestamp +0200\"\n-    datestamp=$(expr \"$datestamp\" + 1)\n-    GIT_AUTHOR_DATE=\"$datestamp +0200\"\n-    datestamp=$(expr \"$datestamp\" + 1)\n-    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n-}\n-\n-test_object_file_size () {\n-\toid=$(git rev-parse \"$1\")\n-\tpath=\".git/objects/$(test_oid_to_path $oid)\"\n-\ttest_file_size \"$path\"\n-}\n-\n-test_expect_success setup '\n-\t# setup .mailmap\n-\tcat >.mailmap <<-EOF &&\n-\tA Thor <athor@example.com> A U Thor <author@example.com>\n-\tC Mitter <cmitter@example.com> C O Mitter <committer@example.com>\n-\tEOF\n-\n-\tsetdate_and_increment &&\n-\techo \"Using $datestamp\" > one &&\n-\tgit add one &&\n-\tgit commit -m \"Initial\" &&\n-\tgit branch -M main &&\n-\tsetdate_and_increment &&\n-\tgit tag -a -m \"Tagging at $datestamp\" testtag &&\n-\tgit update-ref refs/remotes/origin/main main &&\n-\tgit remote add origin nowhere &&\n-\tgit config branch.main.remote origin &&\n-\tgit config branch.main.merge refs/heads/main &&\n-\tgit remote add myfork elsewhere &&\n-\tgit config remote.pushdefault myfork &&\n-\tgit config push.default current\n-'\n-\n-test_atom () {\n-\tcase \"$1\" in\n-\t\thead) ref=refs/heads/main ;;\n-\t\t tag) ref=refs/tags/testtag ;;\n-\t\t sym) ref=refs/heads/sym ;;\n-\t\t   *) ref=$1 ;;\n-\tesac\n-\tformat=$2\n-\ttest_do=test_expect_${4:-success}\n-\n-\tprintf '%s\\n' \"$3\" >expected\n-\t$test_do $PREREQ \"basic atom: $ref $format\" '\n-\t\tgit for-each-ref --format=\"%($format)\" \"$ref\" >actual &&\n-\t\tsanitize_pgp <actual >actual.clean &&\n-\t\ttest_cmp expected actual.clean\n-\t'\n-\n-\t# Automatically test \"contents:size\" atom after testing \"contents\"\n-\tif test \"$format\" = \"contents\"\n-\tthen\n-\t\t# for commit leg, $3 is changed there\n-\t\texpect=$(printf '%s' \"$3\" | wc -c)\n-\t\t$test_do $PREREQ \"basic atom: $ref contents:size\" '\n-\t\t\ttype=$(git cat-file -t \"$ref\") &&\n-\t\t\tcase $type in\n-\t\t\ttag)\n-\t\t\t\t# We cannot use $3 as it expects sanitize_pgp to run\n-\t\t\t\tgit cat-file tag $ref >out &&\n-\t\t\t\texpect=$(tail -n +6 out | wc -c) &&\n-\t\t\t\trm -f out ;;\n-\t\t\ttree | blob)\n-\t\t\t\texpect=\"\" ;;\n-\t\t\tcommit)\n-\t\t\t\t: \"use the calculated expect\" ;;\n-\t\t\t*)\n-\t\t\t\tBUG \"unknown object type\" ;;\n-\t\t\tesac &&\n-\t\t\t# Leave $expect unquoted to lose possible leading whitespaces\n-\t\t\techo $expect >expected &&\n-\t\t\tgit for-each-ref --format=\"%(contents:size)\" \"$ref\" >actual &&\n-\t\t\ttest_cmp expected actual\n-\t\t'\n-\tfi\n-}\n-\n-hexlen=$(test_oid hexsz)\n-\n-test_atom head refname refs/heads/main\n-test_atom head refname: refs/heads/main\n-test_atom head refname:short main\n-test_atom head refname:lstrip=1 heads/main\n-test_atom head refname:lstrip=2 main\n-test_atom head refname:lstrip=-1 main\n-test_atom head refname:lstrip=-2 heads/main\n-test_atom head refname:rstrip=1 refs/heads\n-test_atom head refname:rstrip=2 refs\n-test_atom head refname:rstrip=-1 refs\n-test_atom head refname:rstrip=-2 refs/heads\n-test_atom head refname:strip=1 heads/main\n-test_atom head refname:strip=2 main\n-test_atom head refname:strip=-1 main\n-test_atom head refname:strip=-2 heads/main\n-test_atom head upstream refs/remotes/origin/main\n-test_atom head upstream:short origin/main\n-test_atom head upstream:lstrip=2 origin/main\n-test_atom head upstream:lstrip=-2 origin/main\n-test_atom head upstream:rstrip=2 refs/remotes\n-test_atom head upstream:rstrip=-2 refs/remotes\n-test_atom head upstream:strip=2 origin/main\n-test_atom head upstream:strip=-2 origin/main\n-test_atom head push refs/remotes/myfork/main\n-test_atom head push:short myfork/main\n-test_atom head push:lstrip=1 remotes/myfork/main\n-test_atom head push:lstrip=-1 main\n-test_atom head push:rstrip=1 refs/remotes/myfork\n-test_atom head push:rstrip=-1 refs\n-test_atom head push:strip=1 remotes/myfork/main\n-test_atom head push:strip=-1 main\n-test_atom head objecttype commit\n-test_atom head objectsize $((131 + hexlen))\n-test_atom head objectsize:disk $(test_object_file_size refs/heads/main)\n-test_atom head deltabase $ZERO_OID\n-test_atom head objectname $(git rev-parse refs/heads/main)\n-test_atom head objectname:short $(git rev-parse --short refs/heads/main)\n-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n-test_atom head tree $(git rev-parse refs/heads/main^{tree})\n-test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree})\n-test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree})\n-test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree})\n-test_atom head parent ''\n-test_atom head parent:short ''\n-test_atom head parent:short=1 ''\n-test_atom head parent:short=10 ''\n-test_atom head numparent 0\n-test_atom head object ''\n-test_atom head type ''\n-test_atom head raw \"$(git cat-file commit refs/heads/main)\n-\"\n-test_atom head '*objectname' ''\n-test_atom head '*objecttype' ''\n-test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'\n-test_atom head authorname 'A U Thor'\n-test_atom head authorname:mailmap 'A Thor'\n-test_atom head authoremail '<author@example.com>'\n-test_atom head authoremail:trim 'author@example.com'\n-test_atom head authoremail:localpart 'author'\n-test_atom head authoremail:trim,localpart 'author'\n-test_atom head authoremail:mailmap '<athor@example.com>'\n-test_atom head authoremail:mailmap,trim 'athor@example.com'\n-test_atom head authoremail:trim,mailmap 'athor@example.com'\n-test_atom head authoremail:mailmap,localpart 'athor'\n-test_atom head authoremail:localpart,mailmap 'athor'\n-test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'\n-test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'\n-test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'\n-test_atom head committername 'C O Mitter'\n-test_atom head committername:mailmap 'C Mitter'\n-test_atom head committeremail '<committer@example.com>'\n-test_atom head committeremail:trim 'committer@example.com'\n-test_atom head committeremail:localpart 'committer'\n-test_atom head committeremail:localpart,trim 'committer'\n-test_atom head committeremail:mailmap '<cmitter@example.com>'\n-test_atom head committeremail:mailmap,trim 'cmitter@example.com'\n-test_atom head committeremail:trim,mailmap 'cmitter@example.com'\n-test_atom head committeremail:mailmap,localpart 'cmitter'\n-test_atom head committeremail:localpart,mailmap 'cmitter'\n-test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'\n-test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'\n-test_atom head tag ''\n-test_atom head tagger ''\n-test_atom head taggername ''\n-test_atom head taggeremail ''\n-test_atom head taggeremail:trim ''\n-test_atom head taggeremail:localpart ''\n-test_atom head taggerdate ''\n-test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'\n-test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'\n-test_atom head subject 'Initial'\n-test_atom head subject:sanitize 'Initial'\n-test_atom head contents:subject 'Initial'\n-test_atom head body ''\n-test_atom head contents:body ''\n-test_atom head contents:signature ''\n-test_atom head contents 'Initial\n-'\n-test_atom head HEAD '*'\n-\n-test_atom tag refname refs/tags/testtag\n-test_atom tag refname:short testtag\n-test_atom tag upstream ''\n-test_atom tag push ''\n-test_atom tag objecttype tag\n-test_atom tag objectsize $((114 + hexlen))\n-test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)\n-test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)\n-test_atom tag deltabase $ZERO_OID\n-test_atom tag '*deltabase' $ZERO_OID\n-test_atom tag objectname $(git rev-parse refs/tags/testtag)\n-test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n-test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main)\n-test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main)\n-test_atom tag tree ''\n-test_atom tag tree:short ''\n-test_atom tag tree:short=1 ''\n-test_atom tag tree:short=10 ''\n-test_atom tag parent ''\n-test_atom tag parent:short ''\n-test_atom tag parent:short=1 ''\n-test_atom tag parent:short=10 ''\n-test_atom tag numparent ''\n-test_atom tag object $(git rev-parse refs/tags/testtag^0)\n-test_atom tag type 'commit'\n-test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})\n-test_atom tag '*objecttype' 'commit'\n-test_atom tag author ''\n-test_atom tag authorname ''\n-test_atom tag authorname:mailmap ''\n-test_atom tag authoremail ''\n-test_atom tag authoremail:trim ''\n-test_atom tag authoremail:localpart ''\n-test_atom tag authoremail:trim,localpart ''\n-test_atom tag authoremail:mailmap ''\n-test_atom tag authoremail:mailmap,trim ''\n-test_atom tag authoremail:trim,mailmap ''\n-test_atom tag authoremail:mailmap,localpart ''\n-test_atom tag authoremail:localpart,mailmap ''\n-test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''\n-test_atom tag authordate ''\n-test_atom tag committer ''\n-test_atom tag committername ''\n-test_atom tag committername:mailmap ''\n-test_atom tag committeremail ''\n-test_atom tag committeremail:trim ''\n-test_atom tag committeremail:localpart ''\n-test_atom tag committeremail:localpart,trim ''\n-test_atom tag committeremail:mailmap ''\n-test_atom tag committeremail:mailmap,trim ''\n-test_atom tag committeremail:trim,mailmap ''\n-test_atom tag committeremail:mailmap,localpart ''\n-test_atom tag committeremail:localpart,mailmap ''\n-test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''\n-test_atom tag committerdate ''\n-test_atom tag tag 'testtag'\n-test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'\n-test_atom tag taggername 'C O Mitter'\n-test_atom tag taggername:mailmap 'C Mitter'\n-test_atom tag taggeremail '<committer@example.com>'\n-test_atom tag taggeremail:trim 'committer@example.com'\n-test_atom tag taggeremail:localpart 'committer'\n-test_atom tag taggeremail:trim,localpart 'committer'\n-test_atom tag taggeremail:mailmap '<cmitter@example.com>'\n-test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'\n-test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'\n-test_atom tag taggeremail:mailmap,localpart 'cmitter'\n-test_atom tag taggeremail:localpart,mailmap 'cmitter'\n-test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'\n-test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'\n-test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'\n-test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'\n-test_atom tag subject 'Tagging at 1151968727'\n-test_atom tag subject:sanitize 'Tagging-at-1151968727'\n-test_atom tag contents:subject 'Tagging at 1151968727'\n-test_atom tag body ''\n-test_atom tag contents:body ''\n-test_atom tag contents:signature ''\n-test_atom tag contents 'Tagging at 1151968727\n-'\n-test_atom tag HEAD ' '\n-\n-test_expect_success 'basic atom: refs/tags/testtag *raw' '\n-\tgit cat-file commit refs/tags/testtag^{} >expected &&\n-\tgit for-each-ref --format=\"%(*raw)\" refs/tags/testtag >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_expect_success 'Check invalid atoms names are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'for-each-ref does not crash with -h' '\n+test_expect_success \"for-each-ref does not crash with -h\" '\n \ttest_expect_code 129 git for-each-ref -h >usage &&\n \ttest_grep \"[Uu]sage: git for-each-ref \" usage &&\n \ttest_expect_code 129 nongit git for-each-ref -h >usage &&\n \ttest_grep \"[Uu]sage: git for-each-ref \" usage\n '\n \n-test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n-\tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate) %(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:default) %(authordate:default)\" refs/heads\n-'\n-\n-test_expect_success 'Check valid format specifiers for date fields' '\n-\tgit for-each-ref --format=\"%(authordate:default)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:relative)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:short)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:local)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:iso8601)\" refs/heads &&\n-\tgit for-each-ref --format=\"%(authordate:rfc2822)\" refs/heads\n-'\n-\n-test_expect_success 'Check invalid format specifiers are errors' '\n-\ttest_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n-'\n-\n-test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n-'\n-\n-test_bad_atom () {\n-\tcase \"$1\" in\n-\thead) ref=refs/heads/main ;;\n-\t tag) ref=refs/tags/testtag ;;\n-\t sym) ref=refs/heads/sym ;;\n-\t   *) ref=$1 ;;\n-\tesac\n-\tformat=$2\n-\ttest_do=test_expect_${4:-success}\n-\n-\tprintf '%s\\n' \"$3\" >expect\n-\t$test_do $PREREQ \"err basic atom: $ref $format\" '\n-\t\ttest_must_fail git for-each-ref \\\n-\t\t\t--format=\"%($format)\" \"$ref\" 2>error &&\n-\t\ttest_cmp expect error\n-\t'\n-}\n-\n-test_bad_atom head 'authoremail:foo' \\\n-\t'fatal: unrecognized %(authoremail) argument: foo'\n-\n-test_bad_atom head 'authoremail:mailmap,trim,bar' \\\n-\t'fatal: unrecognized %(authoremail) argument: bar'\n-\n-test_bad_atom head 'authoremail:trim,' \\\n-\t'fatal: unrecognized %(authoremail) argument: '\n-\n-test_bad_atom head 'authoremail:mailmaptrim' \\\n-\t'fatal: unrecognized %(authoremail) argument: trim'\n-\n-test_bad_atom head 'committeremail: ' \\\n-\t'fatal: unrecognized %(committeremail) argument:  '\n-\n-test_bad_atom head 'committeremail: trim,foo' \\\n-\t'fatal: unrecognized %(committeremail) argument:  trim,foo'\n-\n-test_bad_atom head 'committeremail:mailmap,localpart ' \\\n-\t'fatal: unrecognized %(committeremail) argument:  '\n-\n-test_bad_atom head 'committeremail:trim_localpart' \\\n-\t'fatal: unrecognized %(committeremail) argument: _localpart'\n-\n-test_bad_atom head 'committeremail:localpart,,,trim' \\\n-\t'fatal: unrecognized %(committeremail) argument: ,,trim'\n-\n-test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \\\n-\t'fatal: unrecognized %(taggeremail) argument:  foo '\n-\n-test_bad_atom tag 'taggeremail:trim,localpart,' \\\n-\t'fatal: unrecognized %(taggeremail) argument: '\n-\n-test_bad_atom tag 'taggeremail:mailmap;localpart trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument: ;localpart trim'\n-\n-test_bad_atom tag 'taggeremail:localpart trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument:  trim'\n-\n-test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \\\n-\t'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'\n-\n-test_date () {\n-\tf=$1 &&\n-\tcommitter_date=$2 &&\n-\tauthor_date=$3 &&\n-\ttagger_date=$4 &&\n-\tcat >expected <<-EOF &&\n-\t'refs/heads/main' '$committer_date' '$author_date'\n-\t'refs/tags/testtag' '$tagger_date'\n-\tEOF\n-\t(\n-\t\tgit for-each-ref --shell \\\n-\t\t\t--format=\"%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})\" \\\n-\t\t\trefs/heads &&\n-\t\tgit for-each-ref --shell \\\n-\t\t\t--format=\"%(refname) %(taggerdate${f:+:$f})\" \\\n-\t\t\trefs/tags\n-\t) >actual &&\n-\ttest_cmp expected actual\n-}\n-\n-test_expect_success 'Check unformatted date fields output' '\n-\ttest_date \"\" \\\n-\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n-'\n-\n-test_expect_success 'Check format \"default\" formatted date fields output' '\n-\ttest_date default \\\n-\t\t\"Tue Jul 4 01:18:43 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:44 2006 +0200\" \\\n-\t\t\"Tue Jul 4 01:18:45 2006 +0200\"\n-'\n-\n-test_expect_success 'Check format \"default-local\" date fields output' '\n-\ttest_date default-local \"Mon Jul 3 23:18:43 2006\" \"Mon Jul 3 23:18:44 2006\" \"Mon Jul 3 23:18:45 2006\"\n-'\n-\n-# Don't know how to do relative check because I can't know when this script\n-# is going to be run and can't fake the current time to git, and hence can't\n-# provide expected output.  Instead, I'll just make sure that \"relative\"\n-# doesn't exit in error\n-test_expect_success 'Check format \"relative\" date fields output' '\n-\tf=relative &&\n-\t(git for-each-ref --shell --format=\"%(refname) %(committerdate:$f) %(authordate:$f)\" refs/heads &&\n-\tgit for-each-ref --shell --format=\"%(refname) %(taggerdate:$f)\" refs/tags) >actual\n-'\n-\n-# We just check that this is the same as \"relative\" for now.\n-test_expect_success 'Check format \"relative-local\" date fields output' '\n-\ttest_date relative-local \\\n-\t\t\"$(git for-each-ref --format=\"%(committerdate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(authordate:relative)\" refs/heads)\" \\\n-\t\t\"$(git for-each-ref --format=\"%(taggerdate:relative)\" refs/tags)\"\n-'\n-\n-test_expect_success 'Check format \"short\" date fields output' '\n-\ttest_date short 2006-07-04 2006-07-04 2006-07-04\n-'\n-\n-test_expect_success 'Check format \"short-local\" date fields output' '\n-\ttest_date short-local 2006-07-03 2006-07-03 2006-07-03\n-'\n-\n-test_expect_success 'Check format \"local\" date fields output' '\n-\ttest_date local \\\n-\t\t\"Mon Jul 3 23:18:43 2006\" \\\n-\t\t\"Mon Jul 3 23:18:44 2006\" \\\n-\t\t\"Mon Jul 3 23:18:45 2006\"\n-'\n-\n-test_expect_success 'Check format \"iso8601\" date fields output' '\n-\ttest_date iso8601 \\\n-\t\t\"2006-07-04 01:18:43 +0200\" \\\n-\t\t\"2006-07-04 01:18:44 +0200\" \\\n-\t\t\"2006-07-04 01:18:45 +0200\"\n-'\n-\n-test_expect_success 'Check format \"iso8601-local\" date fields output' '\n-\ttest_date iso8601-local \"2006-07-03 23:18:43 +0000\" \"2006-07-03 23:18:44 +0000\" \"2006-07-03 23:18:45 +0000\"\n-'\n-\n-test_expect_success 'Check format \"rfc2822\" date fields output' '\n-\ttest_date rfc2822 \\\n-\t\t\"Tue, 4 Jul 2006 01:18:43 +0200\" \\\n-\t\t\"Tue, 4 Jul 2006 01:18:44 +0200\" \\\n-\t\t\"Tue, 4 Jul 2006 01:18:45 +0200\"\n-'\n-\n-test_expect_success 'Check format \"rfc2822-local\" date fields output' '\n-\ttest_date rfc2822-local \"Mon, 3 Jul 2006 23:18:43 +0000\" \"Mon, 3 Jul 2006 23:18:44 +0000\" \"Mon, 3 Jul 2006 23:18:45 +0000\"\n-'\n-\n-test_expect_success 'Check format \"raw\" date fields output' '\n-\ttest_date raw \"1151968723 +0200\" \"1151968724 +0200\" \"1151968725 +0200\"\n-'\n-\n-test_expect_success 'Check format \"raw-local\" date fields output' '\n-\ttest_date raw-local \"1151968723 +0000\" \"1151968724 +0000\" \"1151968725 +0000\"\n-'\n-\n-test_expect_success 'Check format of strftime date fields' '\n-\techo \"my date is 2006-07-04\" >expected &&\n-\tgit for-each-ref \\\n-\t  --format=\"%(authordate:format:my date is %Y-%m-%d)\" \\\n-\t  refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Check format of strftime-local date fields' '\n-\techo \"my date is 2006-07-03\" >expected &&\n-\tgit for-each-ref \\\n-\t  --format=\"%(authordate:format-local:my date is %Y-%m-%d)\" \\\n-\t  refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'exercise strftime with odd fields' '\n-\techo >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:)\" refs/heads >actual &&\n-\ttest_cmp expected actual &&\n-\tlong=\"long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID\" &&\n-\techo $long >expected &&\n-\tgit for-each-ref --format=\"%(authordate:format:$long)\" refs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/heads/main\n-refs/remotes/origin/main\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'Verify ascending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=refname >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/remotes/origin/main\n-refs/heads/main\n-EOF\n-\n-test_expect_success 'Verify descending sort' '\n-\tgit for-each-ref --format=\"%(refname)\" --sort=-refname >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Give help even with invalid sort atoms' '\n-\ttest_expect_code 129 git for-each-ref --sort=bogus -h >actual 2>&1 &&\n-\tgrep \"^usage: git for-each-ref\" actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/tags/testtag-2\n-EOF\n-\n-test_expect_success 'exercise patterns with prefixes' '\n-\tgit tag testtag-2 &&\n-\ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/testtag refs/tags/testtag-2 >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/testtag\n-refs/tags/testtag-2\n-EOF\n-\n-test_expect_success 'exercise glob patterns with prefixes' '\n-\tgit tag testtag-2 &&\n-\ttest_when_finished \"git tag -d testtag-2\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/testtag \"refs/tags/testtag-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/bar\n-refs/tags/baz\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'exercise patterns with prefix exclusions' '\n-\tfor tag in foo/one foo/two foo/three bar baz\n-\tdo\n-\t\tgit tag \"$tag\" || return 1\n-\tdone &&\n-\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/ --exclude=refs/tags/foo >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-refs/tags/bar\n-refs/tags/baz\n-refs/tags/foo/one\n-refs/tags/testtag\n-EOF\n-\n-test_expect_success 'exercise patterns with pattern exclusions' '\n-\tfor tag in foo/one foo/two foo/three bar baz\n-\tdo\n-\t\tgit tag \"$tag\" || return 1\n-\tdone &&\n-\ttest_when_finished \"git tag -d foo/one foo/two foo/three bar baz\" &&\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\trefs/tags/ --exclude=\"refs/tags/foo/t*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-'refs/heads/main'\n-'refs/remotes/origin/main'\n-'refs/tags/testtag'\n-EOF\n-\n-test_expect_success 'Quoting style: shell' '\n-\tgit for-each-ref --shell --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Quoting style: perl' '\n-\tgit for-each-ref --perl --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Quoting style: python' '\n-\tgit for-each-ref --python --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-\"refs/heads/main\"\n-\"refs/remotes/origin/main\"\n-\"refs/tags/testtag\"\n-EOF\n-\n-test_expect_success 'Quoting style: tcl' '\n-\tgit for-each-ref --tcl --format=\"%(refname)\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n-\ttest_expect_success \"more than one quoting style: $i\" \"\n-\t\ttest_must_fail git for-each-ref $i 2>err &&\n-\t\tgrep '^error: more than one quoting style' err\n-\t\"\n-done\n-\n-test_expect_success 'setup for upstream:track[short]' '\n-\ttest_commit two\n-'\n-\n-test_atom head upstream:track '[ahead 1]'\n-test_atom head upstream:trackshort '>'\n-test_atom head upstream:track,nobracket 'ahead 1'\n-test_atom head upstream:nobracket,track 'ahead 1'\n-\n-test_expect_success 'setup for push:track[short]' '\n-\ttest_commit third &&\n-\tgit update-ref refs/remotes/myfork/main main &&\n-\tgit reset main~1\n-'\n-\n-test_atom head push:track '[behind 1]'\n-test_atom head push:trackshort '<'\n-\n-test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:track)\" 2>/dev/null &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:trackshort)\" 2>/dev/null\n-'\n-\n-test_expect_success 'Check that :track[short] works when upstream is invalid' '\n-\tcat >expected <<-\\EOF &&\n-\t[gone]\n-\n-\tEOF\n-\ttest_when_finished \"git config branch.main.merge refs/heads/main\" &&\n-\tgit config branch.main.merge refs/heads/does-not-exist &&\n-\tgit for-each-ref \\\n-\t\t--format=\"%(upstream:track)$LF%(upstream:trackshort)\" \\\n-\t\trefs/heads >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Check for invalid refname format' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n-'\n-\n-test_expect_success 'set up color tests' '\n-\tcat >expected.color <<-EOF &&\n-\t$(git rev-parse --short refs/heads/main) <GREEN>main<RESET>\n-\t$(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET>\n-\t$(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET>\n-\t$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>\n-\t$(git rev-parse --short refs/tags/third) <GREEN>third<RESET>\n-\t$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>\n-\tEOF\n-\tsed \"s/<[^>]*>//g\" <expected.color >expected.bare &&\n-\tcolor_format=\"%(objectname:short) %(color:green)%(refname:short)\"\n-'\n-\n-test_expect_success TTY '%(color) shows color with a tty' '\n-\ttest_terminal git for-each-ref --format=\"$color_format\" >actual.raw &&\n-\ttest_decode_color <actual.raw >actual &&\n-\ttest_cmp expected.color actual\n-'\n-\n-test_expect_success '%(color) does not show color without tty' '\n-\tTERM=vt100 git for-each-ref --format=\"$color_format\" >actual &&\n-\ttest_cmp expected.bare actual\n-'\n-\n-test_expect_success '--color can override tty check' '\n-\tgit for-each-ref --color --format=\"$color_format\" >actual.raw &&\n-\ttest_decode_color <actual.raw >actual &&\n-\ttest_cmp expected.color actual\n-'\n-\n-test_expect_success 'color.ui=always does not override tty check' '\n-\tgit -c color.ui=always for-each-ref --format=\"$color_format\" >actual &&\n-\ttest_cmp expected.bare actual\n-'\n-\n-test_expect_success 'setup for describe atom tests' '\n-\tgit init -b master describe-repo &&\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\ttest_commit --no-tag one &&\n-\t\tgit tag tagone &&\n-\n-\t\ttest_commit --no-tag two &&\n-\t\tgit tag -a -m \"tag two\" tagtwo\n-\t)\n-'\n-\n-test_expect_success 'describe atom vs git describe' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\tgit for-each-ref --format=\"%(objectname)\" \\\n-\t\t\trefs/tags/ >obj &&\n-\t\twhile read hash\n-\t\tdo\n-\t\t\tif desc=$(git describe $hash)\n-\t\t\tthen\n-\t\t\t\t: >expect-contains-good\n-\t\t\telse\n-\t\t\t\t: >expect-contains-bad\n-\t\t\tfi &&\n-\t\t\techo \"$hash $desc\" || return 1\n-\t\tdone <obj >expect &&\n-\t\ttest_path_exists expect-contains-good &&\n-\t\ttest_path_exists expect-contains-bad &&\n-\n-\t\tgit for-each-ref --format=\"%(objectname) %(describe)\" \\\n-\t\t\trefs/tags/ >actual 2>err &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest_must_be_empty err\n-\t)\n-'\n-\n-test_expect_success 'describe:tags vs describe --tags' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit describe --tags >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:tags)\" \\\n-\t\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\t# Case 1: We have commits between HEAD and the most\n-\t\t#\t  recent tag reachable from it\n-\t\ttest_commit --no-tag file &&\n-\t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual &&\n-\n-\t\t# Make sure the hash used is at least 14 digits long\n-\t\tsed -e \"s/^.*-g\\([0-9a-f]*\\)$/\\1/\" <actual >hexpart &&\n-\t\ttest 15 -le $(wc -c <hexpart) &&\n-\n-\t\t# Case 2: We have a tag at HEAD, describe directly gives\n-\t\t#\t  the name of the tag\n-\t\tgit tag -a -m tagged tagname &&\n-\t\tgit describe --abbrev=14 >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:abbrev=14)\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest tagname = $(cat actual)\n-\t)\n-'\n-\n-test_expect_success 'describe:match=... vs describe --match ...' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit tag -a -m \"tag foo\" tag-foo &&\n-\t\tgit describe --match \"*-foo\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:match=\"*-foo\")\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'describe:exclude:... vs describe --exclude ...' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tgit tag -a -m \"tag bar\" tag-bar &&\n-\t\tgit describe --exclude \"*-bar\" >expect &&\n-\t\tgit for-each-ref --format=\"%(describe:exclude=\"*-bar\")\" \\\n-\t\t\trefs/heads/master >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'deref with describe atom' '\n-\t(\n-\t\tcd describe-repo &&\n-\t\tcat >expect <<-\\EOF &&\n-\n-\t\ttagname\n-\t\ttagname\n-\t\ttagname\n-\n-\t\ttagtwo\n-\t\tEOF\n-\t\tgit for-each-ref --format=\"%(*describe)\" >actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_expect_success 'err on bad describe atom arg' '\n-\t(\n-\t\tcd describe-repo &&\n-\n-\t\t# The bad arg is the only arg passed to describe atom\n-\t\tcat >expect <<-\\EOF &&\n-\t\tfatal: unrecognized %(describe) argument: baz\n-\t\tEOF\n-\t\ttest_must_fail git for-each-ref --format=\"%(describe:baz)\" \\\n-\t\t\trefs/heads/master 2>actual &&\n-\t\ttest_cmp expect actual &&\n-\n-\t\t# The bad arg is in the middle of the option string\n-\t\t# passed to the describe atom\n-\t\tcat >expect <<-\\EOF &&\n-\t\tfatal: unrecognized %(describe) argument: qux=1,abbrev=14\n-\t\tEOF\n-\t\ttest_must_fail git for-each-ref \\\n-\t\t\t--format=\"%(describe:tags,qux=1,abbrev=14)\" \\\n-\t\t\tref/heads/master 2>actual &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-cat >expected <<\\EOF\n-heads/main\n-tags/main\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs (strict)' '\n-\tgit config --bool core.warnambiguousrefs true &&\n-\tgit checkout -b newtag &&\n-\techo \"Using $datestamp\" > one &&\n-\tgit add one &&\n-\tgit commit -m \"Branch\" &&\n-\tsetdate_and_increment &&\n-\tgit tag -m \"Tagging at $datestamp\" main &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-heads/main\n-main\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs (loose)' '\n-\tgit config --bool core.warnambiguousrefs false &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/main refs/tags/main >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<\\EOF\n-heads/ambiguous\n-ambiguous\n-EOF\n-\n-test_expect_success 'Check ambiguous head and tag refs II (loose)' '\n-\tgit checkout main &&\n-\tgit tag ambiguous testtag^0 &&\n-\tgit branch ambiguous testtag^0 &&\n-\tgit for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'create tag without tagger' '\n-\tgit tag -a -m \"Broken tag\" taggerless &&\n-\tgit tag -f taggerless $(git cat-file tag taggerless |\n-\t\tsed -e \"/^tagger /d\" |\n-\t\tgit hash-object --literally --stdin -w -t tag)\n-'\n-\n-test_atom refs/tags/taggerless type 'commit'\n-test_atom refs/tags/taggerless tag 'taggerless'\n-test_atom refs/tags/taggerless tagger ''\n-test_atom refs/tags/taggerless taggername ''\n-test_atom refs/tags/taggerless taggeremail ''\n-test_atom refs/tags/taggerless taggeremail:trim ''\n-test_atom refs/tags/taggerless taggeremail:localpart ''\n-test_atom refs/tags/taggerless taggerdate ''\n-test_atom refs/tags/taggerless committer ''\n-test_atom refs/tags/taggerless committername ''\n-test_atom refs/tags/taggerless committeremail ''\n-test_atom refs/tags/taggerless committeremail:trim ''\n-test_atom refs/tags/taggerless committeremail:localpart ''\n-test_atom refs/tags/taggerless committerdate ''\n-test_atom refs/tags/taggerless subject 'Broken tag'\n-\n-test_expect_success 'an unusual tag with an incomplete line' '\n-\n-\tgit tag -m \"bogo\" bogo &&\n-\tbogo=$(git cat-file tag bogo) &&\n-\tbogo=$(printf \"%s\" \"$bogo\" | git mktag) &&\n-\tgit tag -f bogo \"$bogo\" &&\n-\tgit for-each-ref --format \"%(body)\" refs/tags/bogo\n-\n-'\n-\n-test_expect_success 'create tag with subject and body content' '\n-\tcat >>msg <<-\\EOF &&\n-\t\tthe subject line\n-\n-\t\tfirst body line\n-\t\tsecond body line\n-\tEOF\n-\tgit tag -F msg subject-body\n-'\n-test_atom refs/tags/subject-body subject 'the subject line'\n-test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'\n-test_atom refs/tags/subject-body body 'first body line\n-second body line\n-'\n-test_atom refs/tags/subject-body contents 'the subject line\n-\n-first body line\n-second body line\n-'\n-\n-test_expect_success 'create tag with multiline subject' '\n-\tcat >msg <<-\\EOF &&\n-\t\tfirst subject line\n-\t\tsecond subject line\n-\n-\t\tfirst body line\n-\t\tsecond body line\n-\tEOF\n-\tgit tag -F msg multiline\n-'\n-test_atom refs/tags/multiline subject 'first subject line second subject line'\n-test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'\n-test_atom refs/tags/multiline contents:subject 'first subject line second subject line'\n-test_atom refs/tags/multiline body 'first body line\n-second body line\n-'\n-test_atom refs/tags/multiline contents:body 'first body line\n-second body line\n-'\n-test_atom refs/tags/multiline contents:signature ''\n-test_atom refs/tags/multiline contents 'first subject line\n-second subject line\n-\n-first body line\n-second body line\n-'\n-\n-test_expect_success GPG 'create signed tags' '\n-\tgit tag -s -m \"\" signed-empty &&\n-\tgit tag -s -m \"subject line\" signed-short &&\n-\tcat >msg <<-\\EOF &&\n-\tsubject line\n-\n-\tbody contents\n-\tEOF\n-\tgit tag -s -F msg signed-long\n-'\n-\n-sig='-----BEGIN PGP SIGNATURE-----\n------END PGP SIGNATURE-----\n-'\n-\n-PREREQ=GPG\n-test_atom refs/tags/signed-empty subject ''\n-test_atom refs/tags/signed-empty subject:sanitize ''\n-test_atom refs/tags/signed-empty contents:subject ''\n-test_atom refs/tags/signed-empty body \"$sig\"\n-test_atom refs/tags/signed-empty contents:body ''\n-test_atom refs/tags/signed-empty contents:signature \"$sig\"\n-test_atom refs/tags/signed-empty contents \"$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' '\n-\tgit cat-file tag refs/tags/signed-empty >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-empty >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_atom refs/tags/signed-short subject 'subject line'\n-test_atom refs/tags/signed-short subject:sanitize 'subject-line'\n-test_atom refs/tags/signed-short contents:subject 'subject line'\n-test_atom refs/tags/signed-short body \"$sig\"\n-test_atom refs/tags/signed-short contents:body ''\n-test_atom refs/tags/signed-short contents:signature \"$sig\"\n-test_atom refs/tags/signed-short contents \"subject line\n-$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-short raw' '\n-\tgit cat-file tag refs/tags/signed-short >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-short >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_atom refs/tags/signed-long subject 'subject line'\n-test_atom refs/tags/signed-long subject:sanitize 'subject-line'\n-test_atom refs/tags/signed-long contents:subject 'subject line'\n-test_atom refs/tags/signed-long body \"body contents\n-$sig\"\n-test_atom refs/tags/signed-long contents:body 'body contents\n-'\n-test_atom refs/tags/signed-long contents:signature \"$sig\"\n-test_atom refs/tags/signed-long contents \"subject line\n-\n-body contents\n-$sig\"\n-\n-test_expect_success GPG 'basic atom: refs/tags/signed-long raw' '\n-\tgit cat-file tag refs/tags/signed-long >expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/tags/signed-long >actual &&\n-\tsanitize_pgp <expected >expected.clean &&\n-\techo >>expected.clean &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\ttest_cmp expected.clean actual.clean\n-'\n-\n-test_expect_success 'set up refs pointing to tree and blob' '\n-\tgit update-ref refs/mytrees/first refs/heads/main^{tree} &&\n-\tgit update-ref refs/myblobs/first refs/heads/main:one\n-'\n-\n-test_atom refs/mytrees/first subject \"\"\n-test_atom refs/mytrees/first contents:subject \"\"\n-test_atom refs/mytrees/first body \"\"\n-test_atom refs/mytrees/first contents:body \"\"\n-test_atom refs/mytrees/first contents:signature \"\"\n-test_atom refs/mytrees/first contents \"\"\n-\n-test_expect_success 'basic atom: refs/mytrees/first raw' '\n-\tgit cat-file tree refs/mytrees/first >expected &&\n-\techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual &&\n-\tgit cat-file -s refs/mytrees/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_atom refs/myblobs/first subject \"\"\n-test_atom refs/myblobs/first contents:subject \"\"\n-test_atom refs/myblobs/first body \"\"\n-test_atom refs/myblobs/first contents:body \"\"\n-test_atom refs/myblobs/first contents:signature \"\"\n-test_atom refs/myblobs/first contents \"\"\n-\n-test_expect_success 'basic atom: refs/myblobs/first raw' '\n-\tgit cat-file blob refs/myblobs/first >expected &&\n-\techo >>expected &&\n-\tgit for-each-ref --format=\"%(raw)\" refs/myblobs/first >actual &&\n-\ttest_cmp expected actual &&\n-\tgit cat-file -s refs/myblobs/first >expected &&\n-\tgit for-each-ref --format=\"%(raw:size)\" refs/myblobs/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'set up refs pointing to binary blob' '\n-\tprintf \"a\\0b\\0c\" >blob1 &&\n-\tprintf \"a\\0c\\0b\" >blob2 &&\n-\tprintf \"\\0a\\0b\\0c\" >blob3 &&\n-\tprintf \"abc\" >blob4 &&\n-\tprintf \"\\0 \\0 \\0 \" >blob5 &&\n-\tprintf \"\\0 \\0a\\0 \" >blob6 &&\n-\tprintf \"  \" >blob7 &&\n-\t>blob8 &&\n-\tobj=$(git hash-object -w blob1) &&\n-\tgit update-ref refs/myblobs/blob1 \"$obj\" &&\n-\tobj=$(git hash-object -w blob2) &&\n-\tgit update-ref refs/myblobs/blob2 \"$obj\" &&\n-\tobj=$(git hash-object -w blob3) &&\n-\tgit update-ref refs/myblobs/blob3 \"$obj\" &&\n-\tobj=$(git hash-object -w blob4) &&\n-\tgit update-ref refs/myblobs/blob4 \"$obj\" &&\n-\tobj=$(git hash-object -w blob5) &&\n-\tgit update-ref refs/myblobs/blob5 \"$obj\" &&\n-\tobj=$(git hash-object -w blob6) &&\n-\tgit update-ref refs/myblobs/blob6 \"$obj\" &&\n-\tobj=$(git hash-object -w blob7) &&\n-\tgit update-ref refs/myblobs/blob7 \"$obj\" &&\n-\tobj=$(git hash-object -w blob8) &&\n-\tgit update-ref refs/myblobs/blob8 \"$obj\"\n-'\n-\n-test_expect_success 'Verify sorts with raw' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob8\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/blob3\n-\trefs/myblobs/blob7\n-\trefs/mytrees/first\n-\trefs/myblobs/first\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob4\n-\trefs/heads/main\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw \\\n-\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'Verify sorts with raw:size' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob8\n-\trefs/myblobs/blob7\n-\trefs/myblobs/blob4\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob3\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/first\n-\trefs/mytrees/first\n-\trefs/heads/main\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" --sort=raw:size \\\n-\t\trefs/heads/main refs/myblobs/ refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'validate raw atom with %(if:equals)' '\n-\tcat >expected <<-EOF &&\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\trefs/myblobs/blob4\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tnot equals\n-\tEOF\n-\tgit for-each-ref --format=\"%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)\" \\\n-\t\trefs/myblobs/ refs/heads/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'validate raw atom with %(if:notequals)' '\n-\tcat >expected <<-EOF &&\n-\trefs/heads/ambiguous\n-\trefs/heads/main\n-\trefs/heads/newtag\n-\trefs/myblobs/blob1\n-\trefs/myblobs/blob2\n-\trefs/myblobs/blob3\n-\tequals\n-\trefs/myblobs/blob5\n-\trefs/myblobs/blob6\n-\trefs/myblobs/blob7\n-\trefs/myblobs/blob8\n-\trefs/myblobs/first\n-\tEOF\n-\tgit for-each-ref --format=\"%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)\" \\\n-\t\trefs/myblobs/ refs/heads/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'empty raw refs with %(if)' '\n-\tcat >expected <<-EOF &&\n-\trefs/myblobs/blob1 not empty\n-\trefs/myblobs/blob2 not empty\n-\trefs/myblobs/blob3 not empty\n-\trefs/myblobs/blob4 not empty\n-\trefs/myblobs/blob5 not empty\n-\trefs/myblobs/blob6 not empty\n-\trefs/myblobs/blob7 empty\n-\trefs/myblobs/blob8 empty\n-\trefs/myblobs/first not empty\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)\" \\\n-\t\trefs/myblobs/ >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '%(raw) with --python must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --python\n-'\n-\n-test_expect_success '%(raw) with --tcl must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --tcl\n-'\n-\n-test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob1 --perl | perl >actual &&\n-\tcmp blob1 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob3 --perl | perl >actual &&\n-\tcmp blob3 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/blob8 --perl | perl >actual &&\n-\tcmp blob8 actual &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/myblobs/first --perl | perl >actual &&\n-\tcmp one actual &&\n-\tgit cat-file tree refs/mytrees/first > expected &&\n-\tgit for-each-ref --format=\"\\$name= %(raw);\n-print \\\"\\$name\\\"\" refs/mytrees/first --perl | perl >actual &&\n-\tcmp expected actual\n-'\n-\n-test_expect_success '%(raw) with --shell must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --shell\n-'\n-\n-test_expect_success '%(raw) with --shell and --sort=raw must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(raw)\" --sort=raw --shell\n-'\n-\n-test_expect_success '%(raw:size) with --shell' '\n-\tgit for-each-ref --format=\"%(raw:size)\" | sed \"s/^/$SQ/;s/$/$SQ/\" >expect &&\n-\tgit for-each-ref --format=\"%(raw:size)\" --shell >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --format compare with cat-file --batch' '\n-\tgit rev-parse refs/mytrees/first | git cat-file --batch >expected &&\n-\tgit for-each-ref --format=\"%(objectname) %(objecttype) %(objectsize)\n-%(raw)\" refs/mytrees/first >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'verify sorts with contents:size' '\n-\tcat >expect <<-\\EOF &&\n-\trefs/heads/main\n-\trefs/heads/newtag\n-\trefs/heads/ambiguous\n-\tEOF\n-\tgit for-each-ref --format=\"%(refname)\" \\\n-\t\t--sort=contents:size refs/heads/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'set up multiple-sort tags' '\n-\tfor when in 100000 200000\n-\tdo\n-\t\tfor email in user1 user2\n-\t\tdo\n-\t\t\tfor ref in ref1 ref2\n-\t\t\tdo\n-\t\t\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n-\t\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n-\t\t\t\tgit tag -m \"tag $ref-$when-$email\" \\\n-\t\t\t\tmulti-$ref-$when-$email || return 1\n-\t\t\tdone\n-\t\tdone\n-\tdone\n-'\n-\n-test_expect_success 'Verify sort with multiple keys' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=-refname \\\n-\t\t--sort=taggeremail \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'equivalent sorts fall back on refname' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '--no-sort cancels the previous sort keys' '\n-\tcat >expected <<-\\EOF &&\n-\t100000 <user1@example.com> refs/tags/multi-ref1-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref1-100000-user2\n-\t100000 <user1@example.com> refs/tags/multi-ref2-100000-user1\n-\t100000 <user2@example.com> refs/tags/multi-ref2-100000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref1-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref1-200000-user2\n-\t200000 <user1@example.com> refs/tags/multi-ref2-200000-user1\n-\t200000 <user2@example.com> refs/tags/multi-ref2-200000-user2\n-\tEOF\n-\tgit for-each-ref \\\n-\t\t--format=\"%(taggerdate:unix) %(taggeremail) %(refname)\" \\\n-\t\t--sort=-refname \\\n-\t\t--sort=taggeremail \\\n-\t\t--no-sort \\\n-\t\t--sort=taggerdate \\\n-\t\t\"refs/tags/multi-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success '--no-sort without subsequent --sort prints expected refs' '\n-\tcat >expected <<-\\EOF &&\n-\trefs/tags/multi-ref1-100000-user1\n-\trefs/tags/multi-ref1-100000-user2\n-\trefs/tags/multi-ref1-200000-user1\n-\trefs/tags/multi-ref1-200000-user2\n-\trefs/tags/multi-ref2-100000-user1\n-\trefs/tags/multi-ref2-100000-user2\n-\trefs/tags/multi-ref2-200000-user1\n-\trefs/tags/multi-ref2-200000-user2\n-\tEOF\n-\n-\t# Sort the results with `sort` for a consistent comparison against\n-\t# expected\n-\tgit for-each-ref \\\n-\t\t--format=\"%(refname)\" \\\n-\t\t--no-sort \\\n-\t\t\"refs/tags/multi-*\" | sort >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'set up custom date sorting' '\n-\t# Dates:\n-\t# - Wed Feb 07 2024 21:34:20 +0000\n-\t# - Tue Dec 14 1999 00:05:22 +0000\n-\t# - Fri Jun 04 2021 11:26:51 +0000\n-\t# - Mon Jan 22 2007 16:44:01 GMT+0000\n-\ti=1 &&\n-\tfor when in 1707341660 945129922 1622806011 1169484241\n-\tdo\n-\t\tGIT_COMMITTER_DATE=\"@$when +0000\" \\\n-\t\tGIT_COMMITTER_EMAIL=\"user@example.com\" \\\n-\t\tgit tag -m \"tag $when\" custom-dates-$i &&\n-\t\ti=$(($i+1)) || return 1\n-\tdone\n-'\n-\n-test_expect_success 'sort by date defaults to full timestamp' '\n-\tcat >expected <<-\\EOF &&\n-\t945129922 refs/tags/custom-dates-2\n-\t1169484241 refs/tags/custom-dates-4\n-\t1622806011 refs/tags/custom-dates-3\n-\t1707341660 refs/tags/custom-dates-1\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(creatordate:unix) %(refname)\" \\\n-\t\t--sort=creatordate \\\n-\t\t\"refs/tags/custom-dates-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'sort by custom date format' '\n-\tcat >expected <<-\\EOF &&\n-\t00:05:22 refs/tags/custom-dates-2\n-\t11:26:51 refs/tags/custom-dates-3\n-\t16:44:01 refs/tags/custom-dates-4\n-\t21:34:20 refs/tags/custom-dates-1\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(creatordate:format:%H:%M:%S) %(refname)\" \\\n-\t\t--sort=\"creatordate:format:%H:%M:%S\" \\\n-\t\t\"refs/tags/custom-dates-*\" >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n-\ttest_when_finished \"git checkout main\" &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n-\tsed -e \"s/^\\* /  /\" actual >expect &&\n-\tgit checkout --orphan orphaned-branch &&\n-\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-cat >trailers <<EOF\n-Reviewed-by: A U Thor <author@example.com>\n-Signed-off-by: A U Thor <author@example.com>\n-[ v2 updated patch description ]\n-Acked-by: A U Thor\n-  <author@example.com>\n-EOF\n-\n-unfold () {\n-\tperl -0pe 's/\\n\\s+/ /g'\n-}\n-\n-test_expect_success 'set up trailers for next test' '\n-\techo \"Some contents\" > two &&\n-\tgit add two &&\n-\tgit commit -F - <<-EOF\n-\ttrailers: this commit message has trailers\n-\n-\tSome message contents\n-\n-\t$(cat trailers)\n-\tEOF\n-'\n-\n-test_trailer_option () {\n-\tif test \"$#\" -eq 3\n-\tthen\n-\t\tprereq=\"$1\"\n-\t\tshift\n-\tfi &&\n-\ttitle=$1 option=$2\n-\tcat >expect\n-\ttest_expect_success $prereq \"$title\" '\n-\t\tgit for-each-ref --format=\"%($option)\" refs/heads/main >actual &&\n-\t\ttest_cmp expect actual &&\n-\t\tgit for-each-ref --format=\"%(contents:$option)\" refs/heads/main >actual &&\n-\t\ttest_cmp expect actual\n-\t'\n-}\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \\\n-\t'trailers:unfold' <<-EOF\n-\t$(unfold <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only) shows only \"key: value\" trailers' \\\n-\t'trailers:only' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=no,only=true) shows only \"key: value\" trailers' \\\n-\t'trailers:only=no,only=true' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=yes) shows only \"key: value\" trailers' \\\n-\t'trailers:only=yes' <<-EOF\n-\t$(grep -v patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:only=no) shows all trailers' \\\n-\t'trailers:only=no' <<-EOF\n-\t$(cat trailers)\n-\n-\tEOF\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \\\n-\t'trailers:only,unfold' <<-EOF\n-\t$(grep -v patch.description <trailers | unfold)\n-\n-\tEOF\n-\n-test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \\\n-\t'trailers:unfold,only' <<-EOF\n-\t$(grep -v patch.description <trailers | unfold)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) shows that trailer' \\\n-\t'trailers:key=Signed-off-by' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) is case insensitive' \\\n-\t'trailers:key=SiGned-oFf-bY' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo:) trailing colon also works' \\\n-\t'trailers:key=Signed-off-by:' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) multiple keys' \\\n-\t'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF\n-\tReviewed-by: A U Thor <author@example.com>\n-\tSigned-off-by: A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=nonexistent) becomes empty' \\\n-\t'trailers:key=Shined-off-by:' <<-EOF\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \\\n-\t'trailers:key=Acked-by' <<-EOF\n-\t$(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \\\n-\t'trailers:key=Signed-Off-by,unfold' <<-EOF\n-\t$(unfold <trailers | grep Signed-off-by)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \\\n-\t'trailers:key=Signed-off-by,only=no' <<-EOF\n-\tSigned-off-by: A U Thor <author@example.com>\n-\t$(grep patch.description <trailers)\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \\\n-\t'trailers:key=Signed-off-by,valueonly' <<-EOF\n-\tA U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:separator) changes separator' \\\n-\t'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>\n-\tEOF\n-\n-test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \\\n-\t'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by,A U Thor <author@example.com>\n-\tSigned-off-by,A U Thor <author@example.com>\n-\n-\tEOF\n-\n-test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \\\n-\t'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF\n-\tReviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>\n-\tEOF\n-\n-test_expect_success 'multiple %(trailers) use their own options' '\n-\tgit tag -F - tag-with-trailers <<-\\EOF &&\n-\tbody\n-\n-\tone: foo\n-\tone: bar\n-\ttwo: baz\n-\ttwo: qux\n-\tEOF\n-\tt1=\"%(trailers:key=one,key_value_separator=W,separator=X)\" &&\n-\tt2=\"%(trailers:key=two,key_value_separator=Y,separator=Z)\" &&\n-\tgit for-each-ref --format=\"$t1%0a$t2\" refs/tags/tag-with-trailers >actual &&\n-\tcat >expect <<-\\EOF &&\n-\toneWfooXoneWbar\n-\ttwoYbazZtwoYqux\n-\tEOF\n-\ttest_cmp expect actual\n-'\n-\n-test_failing_trailer_option () {\n-\ttitle=$1 option=$2\n-\tcat >expect\n-\ttest_expect_success \"$title\" '\n-\t\t# error message cannot be checked under i18n\n-\t\ttest_must_fail git for-each-ref --format=\"%($option)\" refs/heads/main 2>actual &&\n-\t\ttest_cmp expect actual &&\n-\t\ttest_must_fail git for-each-ref --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n-\t\ttest_cmp expect actual\n-\t'\n-}\n-\n-test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \\\n-\t'trailers:unsupported' <<-\\EOF\n-\tfatal: unknown %(trailers) argument: unsupported\n-\tEOF\n-\n-test_failing_trailer_option '%(trailers:key) without value is error' \\\n-\t'trailers:key' <<-\\EOF\n-\tfatal: expected %(trailers:key=<value>)\n-\tEOF\n-\n-test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '\n-\tcat >expect <<-EOF &&\n-\tfatal: unrecognized %(contents) argument: trailersonly\n-\tEOF\n-\ttest_must_fail git for-each-ref --format=\"%(contents:trailersonly)\" 2>actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'basic atom: head contents:trailers' '\n-\tgit for-each-ref --format=\"%(contents:trailers)\" refs/heads/main >actual &&\n-\tsanitize_pgp <actual >actual.clean &&\n-\t# git for-each-ref ends with a blank line\n-\tcat >expect <<-EOF &&\n-\t$(cat trailers)\n-\n-\tEOF\n-\ttest_cmp expect actual.clean\n-'\n-\n-test_expect_success 'basic atom: rest must fail' '\n-\ttest_must_fail git for-each-ref --format=\"%(rest)\" refs/heads/main\n-'\n-\n-test_expect_success 'HEAD atom does not take arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(HEAD:foo)\" 2>err &&\n-\techo \"fatal: %(HEAD) does not take arguments\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'subject atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(subject:foo)\" 2>err &&\n-\techo \"fatal: unrecognized %(subject) argument: foo\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'refname atom rejects unknown arguments' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:foo)\" 2>err &&\n-\techo \"fatal: unrecognized %(refname) argument: foo\" >expect &&\n-\ttest_cmp expect err\n-'\n-\n-test_expect_success 'trailer parsing not fooled by --- line' '\n-\tgit commit --allow-empty -F - <<-\\EOF &&\n-\tthis is the subject\n-\n-\tThis is the body. The message has a \"---\" line which would confuse a\n-\tmessage+patch parser. But here we know we have only a commit message,\n-\tso we get it right.\n-\n-\ttrailer: wrong\n-\t---\n-\tThis is more body.\n-\n-\ttrailer: right\n-\tEOF\n-\n-\t{\n-\t\techo \"trailer: right\" &&\n-\t\techo\n-\t} >expect &&\n-\tgit for-each-ref --format=\"%(trailers)\" refs/heads/main >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'Add symbolic ref for the following tests' '\n-\tgit symbolic-ref refs/heads/sym refs/heads/main\n-'\n-\n-cat >expected <<EOF\n-refs/heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref) atom' '\n-\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:short) atom' '\n-\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym >actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-main\n-heads/main\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual &&\n-\n-\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:strip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual\n-'\n-\n-cat >expected <<EOF\n-refs\n-refs/heads\n-EOF\n-\n-test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n-\tgit for-each-ref --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n-\tgit for-each-ref --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n-\ttest_cmp expected actual\n-'\n-\n-test_expect_success ':remotename and :remoteref' '\n-\tgit init remote-tests &&\n-\t(\n-\t\tcd remote-tests &&\n-\t\ttest_commit initial &&\n-\t\tgit branch -M main &&\n-\t\tgit remote add from fifth.coffee:blub &&\n-\t\tgit config branch.main.remote from &&\n-\t\tgit config branch.main.merge refs/heads/stable &&\n-\t\tgit remote add to southridge.audio:repo &&\n-\t\tgit config remote.to.push \"refs/heads/*:refs/heads/pushed/*\" &&\n-\t\tgit config branch.main.pushRemote to &&\n-\t\tfor pair in \"%(upstream)=refs/remotes/from/stable\" \\\n-\t\t\t\"%(upstream:remotename)=from\" \\\n-\t\t\t\"%(upstream:remoteref)=refs/heads/stable\" \\\n-\t\t\t\"%(push)=refs/remotes/to/pushed/main\" \\\n-\t\t\t\"%(push:remotename)=to\" \\\n-\t\t\t\"%(push:remoteref)=refs/heads/pushed/main\"\n-\t\tdo\n-\t\t\techo \"${pair#*=}\" >expect &&\n-\t\t\tgit for-each-ref --format=\"${pair%=*}\" \\\n-\t\t\t\trefs/heads/main >actual &&\n-\t\t\ttest_cmp expect actual || exit 1\n-\t\tdone &&\n-\t\tgit branch push-simple &&\n-\t\tgit config branch.push-simple.pushRemote from &&\n-\t\tactual=\"$(git for-each-ref \\\n-\t\t\t--format=\"%(push:remotename),%(push:remoteref)\" \\\n-\t\t\trefs/heads/push-simple)\" &&\n-\t\ttest from, = \"$actual\"\n-\t)\n-'\n-\n-test_expect_success 'for-each-ref --ignore-case ignores case' '\n-\tgit for-each-ref --format=\"%(refname)\" refs/heads/MAIN >actual &&\n-\ttest_must_be_empty actual &&\n-\n-\techo refs/heads/main >expect &&\n-\tgit for-each-ref --format=\"%(refname)\" --ignore-case \\\n-\t\trefs/heads/MAIN >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --omit-empty works' '\n-\tgit for-each-ref --format=\"%(refname)\" >actual &&\n-\ttest_line_count -gt 1 actual &&\n-\tgit for-each-ref --format=\"%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)\" --omit-empty >actual &&\n-\techo refs/heads/main >expect &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '\n-\t# name refs numerically to avoid case-insensitive filesystem conflicts\n-\tnr=0 &&\n-\tfor email in a A b B\n-\tdo\n-\t\tfor subject in a A b B\n-\t\tdo\n-\t\t\tGIT_COMMITTER_EMAIL=\"$email@example.com\" \\\n-\t\t\tgit tag -m \"tag $subject\" icase-$(printf %02d $nr) &&\n-\t\t\tnr=$((nr+1))||\n-\t\t\treturn 1\n-\t\tdone\n-\tdone &&\n-\tgit for-each-ref --ignore-case \\\n-\t\t--format=\"%(taggeremail) %(subject) %(refname)\" \\\n-\t\t--sort=refname \\\n-\t\t--sort=subject \\\n-\t\t--sort=taggeremail \\\n-\t\trefs/tags/icase-* >actual &&\n-\tcat >expect <<-\\EOF &&\n-\t<a@example.com> tag a refs/tags/icase-00\n-\t<a@example.com> tag A refs/tags/icase-01\n-\t<A@example.com> tag a refs/tags/icase-04\n-\t<A@example.com> tag A refs/tags/icase-05\n-\t<a@example.com> tag b refs/tags/icase-02\n-\t<a@example.com> tag B refs/tags/icase-03\n-\t<A@example.com> tag b refs/tags/icase-06\n-\t<A@example.com> tag B refs/tags/icase-07\n-\t<b@example.com> tag a refs/tags/icase-08\n-\t<b@example.com> tag A refs/tags/icase-09\n-\t<B@example.com> tag a refs/tags/icase-12\n-\t<B@example.com> tag A refs/tags/icase-13\n-\t<b@example.com> tag b refs/tags/icase-10\n-\t<b@example.com> tag B refs/tags/icase-11\n-\t<B@example.com> tag b refs/tags/icase-14\n-\t<B@example.com> tag B refs/tags/icase-15\n-\tEOF\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'for-each-ref reports broken tags' '\n-\tgit tag -m \"good tag\" broken-tag-good HEAD &&\n-\tgit cat-file tag broken-tag-good >good &&\n-\tsed s/commit/blob/ <good >bad &&\n-\tbad=$(git hash-object -w -t tag bad) &&\n-\tgit update-ref refs/tags/broken-tag-bad $bad &&\n-\ttest_must_fail git for-each-ref --format=\"%(*objectname)\" \\\n-\t\trefs/tags/broken-tag-*\n-'\n-\n-test_expect_success 'set up tag with signature and no blank lines' '\n-\tgit tag -F - fake-sig-no-blanks <<-\\EOF\n-\tthis is the subject\n-\t-----BEGIN PGP SIGNATURE-----\n-\tnot a real signature, but we just care about the\n-\tsubject/body parsing. It is important here that\n-\tthere are no blank lines in the signature.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-'\n-\n-test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject'\n-test_atom refs/tags/fake-sig-no-blanks contents:body ''\n-test_atom refs/tags/fake-sig-no-blanks contents:signature \"$sig\"\n-\n-test_expect_success 'set up tag with CRLF signature' '\n-\tappend_cr <<-\\EOF |\n-\tthis is the subject\n-\t-----BEGIN PGP SIGNATURE-----\n-\n-\tnot a real signature, but we just care about\n-\tthe subject/body parsing. It is important here\n-\tthat there is a blank line separating this\n-\tfrom the signature header.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-\tgit tag -F - --cleanup=verbatim fake-sig-crlf\n-'\n-\n-test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject'\n-test_atom refs/tags/fake-sig-crlf contents:body ''\n-\n-# CRLF is retained in the signature, so we have to pass our expected value\n-# through append_cr. But test_atom requires a shell string, which means command\n-# substitution, and the shell will strip trailing newlines from the output of\n-# the substitution. Hack around it by adding and then removing a dummy line.\n-sig_crlf=\"$(printf \"%s\" \"$sig\" | append_cr; echo dummy)\"\n-sig_crlf=${sig_crlf%dummy}\n-test_atom refs/tags/fake-sig-crlf contents:signature \"$sig_crlf\"\n-\n-test_expect_success 'set up tag with signature and trailers' '\n-\tgit tag -F - fake-sig-trailer <<-\\EOF\n-\tthis is the subject\n-\n-\tthis is the body\n-\n-\tMy-Trailer: foo\n-\t-----BEGIN PGP SIGNATURE-----\n-\n-\tnot a real signature, but we just care about the\n-\tsubject/body/trailer parsing.\n-\t-----END PGP SIGNATURE-----\n-\tEOF\n-'\n-\n-# use \"separator=\" here to suppress the terminating newline\n-test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'\n-\n-test_expect_success 'git for-each-ref --stdin: empty' '\n-\t>in &&\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\tgit for-each-ref --format=\"%(refname)\" >expect &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'git for-each-ref --stdin: fails if extra args' '\n-\t>in &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname)\" \\\n-\t\t--stdin refs/heads/extra <in 2>err &&\n-\tgrep \"unknown arguments supplied with --stdin\" err\n-'\n-\n-test_expect_success 'git for-each-ref --stdin: matches' '\n-\tcat >in <<-EOF &&\n-\trefs/tags/multi*\n-\trefs/heads/amb*\n-\tEOF\n-\n-\tcat >expect <<-EOF &&\n-\trefs/heads/ambiguous\n-\trefs/tags/multi-ref1-100000-user1\n-\trefs/tags/multi-ref1-100000-user2\n-\trefs/tags/multi-ref1-200000-user1\n-\trefs/tags/multi-ref1-200000-user2\n-\trefs/tags/multi-ref2-100000-user1\n-\trefs/tags/multi-ref2-100000-user2\n-\trefs/tags/multi-ref2-200000-user1\n-\trefs/tags/multi-ref2-200000-user2\n-\trefs/tags/multiline\n-\tEOF\n-\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'git for-each-ref with non-existing refs' '\n-\tcat >in <<-EOF &&\n-\trefs/heads/this-ref-does-not-exist\n-\trefs/tags/bogus\n-\tEOF\n-\n-\tgit for-each-ref --format=\"%(refname)\" --stdin <in >actual &&\n-\ttest_must_be_empty actual &&\n-\n-\txargs git for-each-ref --format=\"%(refname)\" <in >actual &&\n-\ttest_must_be_empty actual\n-'\n-\n-test_expect_success 'git for-each-ref with nested tags' '\n-\tgit tag -am \"Normal tag\" nested/base HEAD &&\n-\tgit tag -am \"Nested tag\" nested/nest1 refs/tags/nested/base &&\n-\tgit tag -am \"Double nested tag\" nested/nest2 refs/tags/nested/nest1 &&\n-\n-\thead_oid=\"$(git rev-parse HEAD)\" &&\n-\tbase_tag_oid=\"$(git rev-parse refs/tags/nested/base)\" &&\n-\tnest1_tag_oid=\"$(git rev-parse refs/tags/nested/nest1)\" &&\n-\tnest2_tag_oid=\"$(git rev-parse refs/tags/nested/nest2)\" &&\n-\n-\tcat >expect <<-EOF &&\n-\trefs/tags/nested/base $base_tag_oid tag $head_oid commit\n-\trefs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit\n-\trefs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit\n-\tEOF\n-\n-\tgit for-each-ref \\\n-\t\t--format=\"%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)\" \\\n-\t\trefs/tags/nested/ >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'is-base atom with non-commits' '\n-\tgit for-each-ref --format=\"%(is-base:HEAD) %(refname)\" >out 2>err &&\n-\tgrep \"(HEAD) refs/heads/main\" out &&\n-\n-\ttest_line_count = 2 err &&\n-\tgrep \"error: object .* is a commit, not a blob\" err &&\n-\tgrep \"error: bad tag pointer to\" err\n-'\n-\n-GRADE_FORMAT=\"%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n-TRUSTLEVEL_FORMAT=\"%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)\"\n-\n-test_expect_success GPG 'setup for signature atom using gpg' '\n-\tgit checkout -b signed &&\n-\n-\ttest_when_finished \"test_unconfig commit.gpgSign\" &&\n-\n-\techo \"1\" >file &&\n-\tgit add file &&\n-\ttest_tick &&\n-\tgit commit -S -m \"file: 1\" &&\n-\tgit tag first-signed &&\n-\n-\techo \"2\" >file &&\n-\ttest_tick &&\n-\tgit commit -a -m \"file: 2\" &&\n-\tgit tag second-unsigned &&\n-\n-\tgit config commit.gpgSign 1 &&\n-\techo \"3\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 3\" &&\n-\tgit tag third-unsigned &&\n-\n-\ttest_tick &&\n-\tgit rebase -f HEAD^^ && git tag second-signed HEAD^ &&\n-\tgit tag third-signed &&\n-\n-\techo \"4\" >file &&\n-\ttest_tick &&\n-\tgit commit -a -SB7227189 -m \"file: 4\" &&\n-\tgit tag fourth-signed &&\n-\n-\techo \"5\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 5\" &&\n-\tgit tag fifth-unsigned &&\n-\n-\techo \"6\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 6\" &&\n-\n-\ttest_tick &&\n-\tgit rebase -f HEAD^^ &&\n-\tgit tag fifth-signed HEAD^ &&\n-\tgit tag sixth-signed &&\n-\n-\techo \"7\" >file &&\n-\ttest_tick &&\n-\tgit commit -a --no-gpg-sign -m \"file: 7\" &&\n-\tgit tag seventh-unsigned\n-'\n-\n-test_expect_success GPGSSH 'setup for signature atom using ssh' '\n-\ttest_when_finished \"test_unconfig gpg.format user.signingkey\" &&\n-\n-\ttest_config gpg.format ssh &&\n-\ttest_config user.signingkey \"${GPGSSH_KEY_PRIMARY}\" &&\n-\techo \"8\" >file &&\n-\ttest_tick &&\n-\tgit add file &&\n-\tgit commit -S -m \"file: 8\" &&\n-\tgit tag eighth-signed-ssh\n-'\n-\n-test_expect_success GPG2 'bare signature atom' '\n-\tgit verify-commit first-signed 2>expect &&\n-\techo  >>expect &&\n-\tgit for-each-ref refs/tags/first-signed \\\n-\t\t--format=\"%(signature)\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show good signature with custom format' '\n-\tgit verify-commit first-signed &&\n-\tcat >expect <<-\\EOF &&\n-\tG\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\tEOF\n-\tgit for-each-ref refs/tags/first-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-test_expect_success GPGSSH 'show good signature with custom format with ssh' '\n-\ttest_config gpg.ssh.allowedSignersFile \"${GPGSSH_ALLOWED_SIGNERS}\" &&\n-\tFINGERPRINT=$(ssh-keygen -lf \"${GPGSSH_KEY_PRIMARY}\" | awk \"{print \\$2;}\") &&\n-\tcat >expect.tmpl <<-\\EOF &&\n-\tG\n-\tFINGERPRINT\n-\tprincipal with number 1\n-\tFINGERPRINT\n-\n-\tEOF\n-\tsed \"s|FINGERPRINT|$FINGERPRINT|g\" expect.tmpl >expect &&\n-\tgit for-each-ref refs/tags/eighth-signed-ssh \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'signature atom with grade option and bad signature' '\n-\tgit cat-file commit third-signed >raw &&\n-\tsed -e \"s/^file: 3/file: 3 forged/\" raw >forged1 &&\n-\tFORGED1=$(git hash-object -w -t commit forged1) &&\n-\tgit update-ref refs/tags/third-signed \"$FORGED1\" &&\n-\ttest_must_fail git verify-commit \"$FORGED1\" &&\n-\n-\tcat >expect <<-\\EOF &&\n-\tB\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\n-\n-\tEOF\n-\tgit for-each-ref refs/tags/third-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tU\n-\t65A0EEA02E30CAD7\n-\tEris Discordia <discord@example.net>\n-\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n-\tD4BE22311AD3131E5EDA29A461092E85B7227189\n-\tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with undefined trust level' '\n-\tcat >expect <<-\\EOF &&\n-\tundefined\n-\t65A0EEA02E30CAD7\n-\tEris Discordia <discord@example.net>\n-\tF8364A59E07FFE9F4D63005A65A0EEA02E30CAD7\n-\tD4BE22311AD3131E5EDA29A461092E85B7227189\n-\tEOF\n-\tgit for-each-ref refs/tags/fourth-signed \\\n-\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show untrusted signature with ultimate trust level' '\n-\tcat >expect <<-\\EOF &&\n-\tultimate\n-\t13B6F51ECDDE430D\n-\tC O Mitter <committer@example.com>\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\t73D758744BE721698EC54E8713B6F51ECDDE430D\n-\tEOF\n-\tgit for-each-ref refs/tags/sixth-signed \\\n-\t\t--format=\"$TRUSTLEVEL_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show unknown signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tE\n-\t13B6F51ECDDE430D\n-\n-\n-\n-\tEOF\n-\tGNUPGHOME=\"$GNUPGHOME_NOT_USED\" git for-each-ref \\\n-\t\trefs/tags/sixth-signed --format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success GPG 'show lack of signature with custom format' '\n-\tcat >expect <<-\\EOF &&\n-\tN\n-\n-\n-\n-\n-\tEOF\n-\tgit for-each-ref refs/tags/seventh-unsigned \\\n-\t\t--format=\"$GRADE_FORMAT\" >actual &&\n-\ttest_cmp expect actual\n-'\n+. \"$TEST_DIRECTORY\"/for-each-ref-tests.sh\n \n test_done\n-- \n2.34.1\n\n"},{"id":"523522","messageId":"20250805092758.5321-7-meetsoni3017@gmail.com","threadId":"63703","inReplyTo":"20250805092758.5321-1-meetsoni3017@gmail.com","subject":"[GSoC][PATCH v6 6/6] t: add test for git refs list subcommand","fromName":"Meet Soni","fromEmail":"meetsoni3017@gmail.com","sentAt":"2025-08-05T09:27:58Z","receivedAt":"2025-08-05T09:28:31Z","isPatch":true,"sender":{"key":"meetsoni3017@gmail.com","avatar":"https://avatars.githubusercontent.com/u/92802561?v=4"},"body":"Add a test script, `t/t1461-refs-list.sh`, for the new `git refs list`\ncommand.\n\nThis script acts as a simple driver, leveraging the shared test library\ncreated in the preceding commit. It works by overriding the\n`$git_for_each_ref` variable to \"git refs list\" and then sourcing the\nshared library (`t/for-each-ref-tests.sh`).\n\nThis approach ensures that `git refs list` is tested against the\nentire comprehensive test suite of `git for-each-ref`, verifying\nthat it acts as a compatible drop-in replacement.\n\nMentored-by: Patrick Steinhardt <ps@pks.im>\nMentored-by: shejialuo <shejialuo@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Meet Soni <meetsoni3017@gmail.com>\n---\n t/meson.build        | 1 +\n t/t1461-refs-list.sh | 8 ++++++++\n 2 files changed, 9 insertions(+)\n create mode 100755 t/t1461-refs-list.sh\n\ndiff --git a/t/meson.build b/t/meson.build\nindex bbeba1a8d5..23a3702958 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -208,6 +208,7 @@ integration_tests = [\n   't1450-fsck.sh',\n   't1451-fsck-buffer.sh',\n   't1460-refs-migrate.sh',\n+  't1461-refs-list.sh',\n   't1500-rev-parse.sh',\n   't1501-work-tree.sh',\n   't1502-rev-parse-parseopt.sh',\ndiff --git a/t/t1461-refs-list.sh b/t/t1461-refs-list.sh\nnew file mode 100755\nindex 0000000000..36e3d81e59\n--- /dev/null\n+++ b/t/t1461-refs-list.sh\n@@ -0,0 +1,8 @@\n+#!/bin/sh\n+\n+test_description='git refs list tests'\n+\n+. ./test-lib.sh\n+\n+git_for_each_ref='git refs list'\n+. \"$TEST_DIRECTORY\"/for-each-ref-tests.sh\n-- \n2.34.1\n\n"},{"id":"523545","messageId":"aJICDeyvDjt569GX@pks.im","threadId":"63703","inReplyTo":"20250805092758.5321-1-meetsoni3017@gmail.com","subject":"Re: [GSoC][PATCH v6 0/6] Add refs list subcommand","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-05T13:07:25Z","receivedAt":"2025-08-05T13:07:31Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Aug 05, 2025 at 02:57:52PM +0530, Meet Soni wrote:\n> Hello everyone,\n> \n> This is the sixth version of the patch series that introduces the git\n> refs list subcommand.\n> \n> changes in v6:\n>   - rebased onto v2.51.0-rc0\n\nI'm happy with this version, thanks!\n\nPatrick\n"},{"id":"523566","messageId":"xmqqy0rx21dm.fsf@gitster.g","threadId":"63703","inReplyTo":"20250805092758.5321-1-meetsoni3017@gmail.com","subject":"Re: [GSoC][PATCH v6 0/6] Add refs list subcommand","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-05T16:12:53Z","receivedAt":"2025-08-05T16:12:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Meet Soni <meetsoni3017@gmail.com> writes:\n\n> Hello everyone,\n>\n> This is the sixth version of the patch series that introduces the git\n> refs list subcommand.\n>\n> changes in v6:\n>   - rebased onto v2.51.0-rc0\n\nThanks.  Will queue.\n"}]}