{"thread":{"id":"63618","subject":"[GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","startedAt":"2025-06-10T15:21:47Z","lastAt":"2025-08-17T16:21:27Z","messageCount":226,"participants":["Lucas Seiki Oshiro","Kristoffer Haugsbakk","Junio C Hamano","Karthik Nayak","Phillip Wood","phillip.wood123@gmail.com","Patrick Steinhardt","Justin Tobler","Oswald Buddenhagen","Jean-Noël AVILA","Eric Sunshine","Ben Knoble"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"520044","messageId":"20250610152117.14826-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":null,"subject":"[GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-10T15:21:12Z","receivedAt":"2025-06-10T15:21:47Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\nThis is the first RFC for my GSoC project: the new command `repo-info`.\n\n## Motivation\n\nCurrently, `git rev-parse` covers a wide range of functionality not directly\nrelated to parsing revisions, as its name says. Over time, many features like\nparsing datestrings, options, paths, and others were added to it because\nthere wasn't a more appropriated command to place them.\n\nThis way, many of these features would be better placed in new and dedicated\ncommands. This kind of movement had other precedents in Git, for example, `git\nswitch` and `git restore` were created after `git checkout` became too\noverloaded.\n\n## Proposal\n\nThis patchset introduces a new command `git repo-info`. Its goal is to bring the\nfunctionality of retrieving repository-related information currently returned by\n`rev-parse`, displaying them in machine-readable formats (like JSON or plaintext).\n\nUnder `rev-parse`, these information is retrieved by using these flags\n(described in the section \"Options for Files\" of `rev-parse`'s documentation):\n\n- `--show-object-format`: the hashing algorithm (i.e. `sha1` or `sha256`)\n\n- `--git-dir`: The Git directory of the repository\n\n- `--git-common-dir`: The common Git directory\n\n- `--show-toplevel`: the top level directory of the repository\n\n- `--show-ref-format`: the reference database format (`files` or `reftable`),\n\n- `--show-superproject-working-tree`: the absolute path of the superproject\n\n- `--is-bare-repository`: whether this is a bare repository\n\n- `--is-shallow-repository` whether this is a shallow repository\n\n## Command design\n\nThe retrieved data will be presented as in a JSON format, like this:\n\n~~~\n$ git repo-info\n{\n  \"objects\": {\n    \"format\": \"sha1\"\n  },\n  \"references\": {\n    \"format\": \"files\"\n  },\n  \"path\": {\n    \"git-dir\": \"/git/dir\"\n    \"git-commom-dir\": \"/git/common-dir\",\n    \"toplevel\": \"/git/toplevel\",\n    \"superproject-working-tree\": \"/super/working/tree\",\n  }\n  \"layout\": {\n    \"bare\": false,\n    \"shallow\": false\n  }\n}\n~~~\n\nOr in a plaintext format, like this:\n\n~~~\n$ git repo-info --format=plaintext\nsha1\nfiles\n/git/dir\n/git/common-dir\n/git/toplevel\n/super/working/tree\nfalse\nfalse\n~~~\n\nIt will also allow the user to get only the desired fields, like this:\n\n~~~\n$ git repo-info --format=plaintext objects.format references.format\nsha1\nfiles\n~~~\n\nor:\n\n~~~\n$ git repo-info objects.format references.format\n{\n  \"objects\": {\n    \"format\": \"sha1\"\n  },\n  \"references\": {\n    \"format\": \"files\"\n  }\n}\n~~~\n\nCurrently this RFC only implements \"objects.format\", \"layout.bare\" and\n\"layout.shallow\", but I'm making it flexible to add other fields, following\nthis format.\n\n## Feedback\n\nI would like to ask for your feedback on this proprosal, specially:\n\n- on deciding if the JSON and linewise plaintext formats are the really the best,\n  or if I should consider others (e.g. gitconfig syntax, NUL-terminated, etc)\n\n- on deciding how the fields will be specified. This \"<category>.<format>\" was\n  a first idea based on the JSON structure\n\n- about the JSON schema\n\n- about information that may be nice to include in the output of this command,\n  even if they are not currently retrieved by `rev-parse`\n\nThanks!\n\nLucas Seiki Oshiro (5):\n  repo-info: declare the repo-info command\n  repo-info: add the --format flag\n  repo-info: add the field references.format\n  repo-info: add field layout.bare\n  repo-info: add field layout.shallow\n\n .gitignore           |   1 +\n Makefile             |   1 +\n builtin.h            |   1 +\n builtin/repo-info.c  | 233 +++++++++++++++++++++++++++++++++++++++++++\n git.c                |   1 +\n meson.build          |   1 +\n t/meson.build        |   1 +\n t/t1518-repo-info.sh | 123 +++++++++++++++++++++++\n 8 files changed, 362 insertions(+)\n create mode 100644 builtin/repo-info.c\n create mode 100755 t/t1518-repo-info.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520045","messageId":"20250610152117.14826-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH 1/5] repo-info: declare the repo-info command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-10T15:21:13Z","receivedAt":"2025-06-10T15:21:49Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Create a new Git subcommand called repo-info. `git repo-info` will query\nmetadata from the current repository and outputs it as JSON or plaintext.\n\nAlso add entries for this new command in:\n\n- the build files (Makefile and meson.build)\n- builtin.h\n- git.c\n- .gitignore\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore          |  1 +\n Makefile            |  1 +\n builtin.h           |  1 +\n builtin/repo-info.c | 22 ++++++++++++++++++++++\n git.c               |  1 +\n meson.build         |  1 +\n 6 files changed, 27 insertions(+)\n create mode 100644 builtin/repo-info.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..b2f3fb0047 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo-info\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Makefile b/Makefile\nindex 70d1543b6b..50e3a3cbcc 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1308,6 +1308,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo-info.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..cc6bc95962 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo_info(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nnew file mode 100644\nindex 0000000000..4615b988d8\n--- /dev/null\n+++ b/builtin/repo-info.c\n@@ -0,0 +1,22 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+int cmd_repo_info(\n+\tint argc,\n+\tconst char **argv,\n+\tconst char *prefix,\n+\tstruct repository *repo UNUSED\n+\t)\n+{\n+\tconst char *const repo_info_usage[] = {\n+\t\t\"git repo-info\",\n+\t\tNULL\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_info_usage, 0);\n+\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 7c37872a88..d1774de82f 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo-info\", cmd_repo_info, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 7fea4a34d6..06f2f647ba 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo-info.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520046","messageId":"20250610152117.14826-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH 2/5] repo-info: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-10T15:21:14Z","receivedAt":"2025-06-10T15:21:52Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the --format flag to the repo-info command, allowing the user to\nchoose betwen the 'json' and 'plaintext' formats as output.\n\nAlso add a flag --allow-empty, which will force the output data to be\nempty when no field is requested.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 79 ++++++++++++++++++++++++++++++++++++++++++--\n t/meson.build        |  1 +\n t/t1518-repo-info.sh | 49 +++++++++++++++++++++++++++\n 3 files changed, 127 insertions(+), 2 deletions(-)\n create mode 100755 t/t1518-repo-info.sh\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex 4615b988d8..4d539a17fb 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -1,22 +1,97 @@\n #include \"builtin.h\"\n+#include \"hash.h\"\n+#include \"json-writer.h\"\n #include \"parse-options.h\"\n+#include \"refs.h\"\n+\n+enum output_format {\n+\tFORMAT_PLAINTEXT,\n+\tFORMAT_JSON\n+};\n+\n+struct repo_info {\n+\tstruct repository *repo;\n+\tenum output_format format;\n+};\n+\n+static void repo_info_init(struct repo_info *repo_info,\n+\t\t\t   struct repository *repo,\n+\t\t\t   char *format,\n+\t\t\t   int allow_empty UNUSED,\n+\t\t\t   int argc UNUSED,\n+\t\t\t   const char **argv UNUSED\n+\t\t\t   ) {\n+\trepo_info->repo = repo;\n+\n+\tif (format == NULL || !strcmp(format, \"json\"))\n+\t\trepo_info->format = FORMAT_JSON;\n+\telse if (!strcmp(format, \"plaintext\"))\n+\t\trepo_info->format = FORMAT_PLAINTEXT;\n+\telse\n+\t\tdie(\"invalid format %s\", format);\n+}\n+\n+static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED) {\n+}\n+\n+static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n+{\n+\tstruct json_writer jw;\n+\n+\tjw_init(&jw);\n+\n+\tjw_object_begin(&jw, 1);\n+\tjw_end(&jw);\n+\n+\tputs(jw.json.buf);\n+\tjw_release(&jw);\n+}\n+\n+static void repo_info_print(struct repo_info *repo_info)\n+{\n+\tenum output_format format = repo_info->format;\n+\n+\tswitch (format) {\n+\tcase FORMAT_PLAINTEXT:\n+\t\trepo_info_print_plaintext(repo_info);\n+\t\tbreak;\n+\tcase FORMAT_JSON:\n+\t\trepo_info_print_json(repo_info);\n+\t\tbreak;\n+\t}\n+}\n \n int cmd_repo_info(\n \tint argc,\n \tconst char **argv,\n \tconst char *prefix,\n-\tstruct repository *repo UNUSED\n+\tstruct repository *repo\n \t)\n {\n \tconst char *const repo_info_usage[] = {\n \t\t\"git repo-info\",\n \t\tNULL\n \t};\n+\tstruct repo_info repo_info;\n+\tchar *format = NULL;\n+\tint allow_empty = 0;\n \tstruct option options[] = {\n+\t\tOPT_STRING(0,\n+\t\t\t     \"format\",\n+\t\t\t     &format,\n+\t\t\t     N_(\"format\"),\n+\t\t\t     N_(\"output format\")),\n+\t\tOPT_BOOL(0,\n+\t\t\t \"allow-empty\",\n+\t\t\t &allow_empty,\n+\t\t\t \"when set, it will use an empty set of fields if no field is requested\"),\n \t\tOPT_END()\n \t};\n \n-\targc = parse_options(argc, argv, prefix, options, repo_info_usage, 0);\n+\targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n+\t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n+\trepo_info_init(&repo_info, repo, format, allow_empty, argc, argv);\n+\trepo_info_print(&repo_info);\n \n \treturn 0;\n }\ndiff --git a/t/meson.build b/t/meson.build\nindex 50e89e764a..c24e197c60 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -241,6 +241,7 @@ integration_tests = [\n   't1514-rev-parse-push.sh',\n   't1515-rev-parse-outside-repo.sh',\n   't1517-outside-repo.sh',\n+  't1518-repo-info.sh',\n   't1600-index.sh',\n   't1601-index-bogus.sh',\n   't1700-split-index.sh',\ndiff --git a/t/t1518-repo-info.sh b/t/t1518-repo-info.sh\nnew file mode 100755\nindex 0000000000..2e1a6f0c34\n--- /dev/null\n+++ b/t/t1518-repo-info.sh\n@@ -0,0 +1,49 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+parse_json () {\n+\ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n+}\n+\n+test_repo_info () {\n+\tlabel=$1\n+\tinit_args=$2\n+\tkey=$3\n+\texpected_value=$4\n+\n+\ttest_expect_success \"json: $label\" \"\n+\t\ttest_when_finished 'rm -rf repo' &&\n+\t\tgit init $init_args repo &&\n+\t\tcd repo &&\n+\t\techo '$expected_value' >expect &&\n+\t\tgit repo-info '$key'| parse_json >output &&\n+\t\tgrep -F 'row[0].$key' output | cut -d ' ' -f 2 >actual &&\n+\t\ttest_cmp expect actual\n+\t\"\n+\n+\ttest_expect_success \"plaintext: $label\" \"\n+\t\ttest_when_finished 'rm -rf repo' &&\n+\t\tgit init $init_args repo &&\n+\t\tcd repo &&\n+\t\techo '$expected_value' >expect &&\n+\t\tgit repo-info --format=plaintext '$key' >actual &&\n+\t\ttest_cmp expect actual\n+\t\"\n+}\n+\n+test_expect_success 'json: returns empty output with allow-empty' '\n+\tgit repo-info --allow-empty --format=json >output &&\n+\ttest_line_count = 2 output\n+'\n+\n+test_expect_success 'plaintext: returns empty output with allow-empty' '\n+\tgit repo-info --allow-empty --format=plaintext >output &&\n+\ttest_line_count = 0 output\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520047","messageId":"20250610152117.14826-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH 3/5] repo-info: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-10T15:21:15Z","receivedAt":"2025-06-10T15:21:54Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the field references.format to the repo-info command. The data\nretrieved in this field is the same that currently is obtained by\nrunning `git rev-parse --show-ref-format`.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 97 +++++++++++++++++++++++++++++++++++++++++---\n t/t1518-repo-info.sh | 20 +++++++++\n 2 files changed, 112 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex 4d539a17fb..a1c9d3942e 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -9,18 +9,40 @@ enum output_format {\n \tFORMAT_JSON\n };\n \n+enum repo_info_category {\n+\tCATEGORY_REFERENCES = 1\n+};\n+\n+enum repo_info_references_field {\n+\tFIELD_REFERENCES_FORMAT = 1\n+};\n+\n+struct repo_info_field {\n+\tenum repo_info_category category;\n+\tunion {\n+\t\tenum repo_info_references_field references;\n+\t} field;\n+};\n+\n struct repo_info {\n \tstruct repository *repo;\n \tenum output_format format;\n+\tint n_fields;\n+\tstruct repo_info_field *fields;\n+};\n+\n+const char *default_fields[] = {\n+\t\"references.format\",\n };\n \n static void repo_info_init(struct repo_info *repo_info,\n \t\t\t   struct repository *repo,\n \t\t\t   char *format,\n-\t\t\t   int allow_empty UNUSED,\n-\t\t\t   int argc UNUSED,\n-\t\t\t   const char **argv UNUSED\n+\t\t\t   int allow_empty,\n+\t\t\t   int argc,\n+\t\t\t   const char **argv\n \t\t\t   ) {\n+\tint i;\n \trepo_info->repo = repo;\n \n \tif (format == NULL || !strcmp(format, \"json\"))\n@@ -29,18 +51,82 @@ static void repo_info_init(struct repo_info *repo_info,\n \t\trepo_info->format = FORMAT_PLAINTEXT;\n \telse\n \t\tdie(\"invalid format %s\", format);\n+\n+\tif (argc == 0 && !allow_empty) {\n+\t\targc = ARRAY_SIZE(default_fields);\n+\t\targv = default_fields;\n+\t}\n+\n+\trepo_info->n_fields = argc;\n+\trepo_info->fields = xmalloc(argc * sizeof(struct repo_info_field));\n+\n+\tfor (i = 0; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\t\tstruct repo_info_field *field = repo_info->fields + i;\n+\n+\t\tif (!strcmp(arg, \"references.format\")) {\n+\t\t\tfield->category = CATEGORY_REFERENCES;\n+\t\t\tfield->field.references = FIELD_REFERENCES_FORMAT;\n+\t\t}\n+\t\telse {\n+\t\t\tdie(\"invalid field '%s'\", arg);\n+\t\t}\n+\t}\n+}\n+\n+static void repo_info_release(struct repo_info *repo_info) {\n+\tfree(repo_info->fields);\n }\n \n-static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED) {\n+static void repo_info_print_plaintext(struct repo_info *repo_info) {\n+\tstruct repository *repo = repo_info->repo;\n+\tint i;\n+\tfor (i = 0; i < repo_info->n_fields; i++) {\n+\t\tstruct repo_info_field *field = &repo_info->fields[i];\n+\t\tswitch (field->category) {\n+\t\tcase CATEGORY_REFERENCES:\n+\t\t\tswitch (field->field.references) {\n+\t\t\tcase FIELD_REFERENCES_FORMAT:\n+\t\t\t\tputs(ref_storage_format_to_name(\n+\t\t\t\t\trepo->ref_storage_format));\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t}\n }\n \n-static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n+static void repo_info_print_json(struct repo_info *repo_info)\n {\n \tstruct json_writer jw;\n+\tint i;\n+\tunsigned int categories = 0;\n+\tunsigned int references_fields = 0;\n+\tstruct repository *repo = repo_info->repo;\n+\n+\tfor (i = 0; i < repo_info->n_fields; i++) {\n+\t\tstruct repo_info_field *field = repo_info->fields + i;\n+\t\tcategories |= field->category;\n+\t\tswitch (field->category) {\n+\t\tcase CATEGORY_REFERENCES:\n+\t\t\treferences_fields |= field->field.references;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n \n \tjw_init(&jw);\n \n \tjw_object_begin(&jw, 1);\n+\n+\tif (categories & CATEGORY_REFERENCES) {\n+\t\tjw_object_inline_begin_object(&jw, \"references\");\n+\t\tif (references_fields & FIELD_REFERENCES_FORMAT) {\n+\t\t\tconst char *format_name = ref_storage_format_to_name(\n+\t\t\t\trepo->ref_storage_format);\n+\t\t\tjw_object_string(&jw, \"format\", format_name);\n+\t\t}\n+\t\tjw_end(&jw);\n+\t}\n \tjw_end(&jw);\n \n \tputs(jw.json.buf);\n@@ -92,6 +178,7 @@ int cmd_repo_info(\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n \trepo_info_init(&repo_info, repo, format, allow_empty, argc, argv);\n \trepo_info_print(&repo_info);\n+\trepo_info_release(&repo_info);\n \n \treturn 0;\n }\ndiff --git a/t/t1518-repo-info.sh b/t/t1518-repo-info.sh\nindex 2e1a6f0c34..a99198b0f6 100755\n--- a/t/t1518-repo-info.sh\n+++ b/t/t1518-repo-info.sh\n@@ -6,6 +6,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n+DEFAULT_NUMBER_OF_FIELDS=1\n+\n parse_json () {\n \ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n }\n@@ -46,4 +48,22 @@ test_expect_success 'plaintext: returns empty output with allow-empty' '\n \ttest_line_count = 0 output\n '\n \n+test_repo_info 'ref format files is retrieved correctly' \\\n+\t'' \\\n+\t'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' \\\n+\t'--ref-format=reftable' \\\n+\t'references.format' 'reftable'\n+\n+test_expect_success 'plaintext: output all default fields' \"\n+\tgit repo-info --format=plaintext >actual &&\n+\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n+\"\n+\n+test_expect_success 'json: output all default fields' \"\n+\tgit repo-info --format=json | parse_json | grep '.*\\..*\\..*' >actual &&\n+\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n+\"\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520048","messageId":"20250610152117.14826-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH 5/5] repo-info: add field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-10T15:21:17Z","receivedAt":"2025-06-10T15:21:58Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the field layout.shallow to the repo-info command. The data\nretrieved in this field is the same that currently is obtained by\nrunning `git rev-parse --is-shallow-repository`.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 20 ++++++++++++++++--\n t/t1518-repo-info.sh | 48 +++++++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 65 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex bc25a0809f..d821292d78 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -6,6 +6,7 @@\n #include \"json-writer.h\"\n #include \"parse-options.h\"\n #include \"refs.h\"\n+#include \"shallow.h\"\n \n enum output_format {\n \tFORMAT_PLAINTEXT,\n@@ -21,7 +22,9 @@ enum repo_info_references_field {\n \tFIELD_REFERENCES_FORMAT = 1\n };\n \n-enum repo_info_layout_field { FIELD_LAYOUT_BARE = 1\n+enum repo_info_layout_field {\n+\tFIELD_LAYOUT_BARE = 1,\n+\tFIELD_LAYOUT_SHALLOW = 1 << 1\n };\n \n struct repo_info_field {\n@@ -41,7 +44,8 @@ struct repo_info {\n \n const char *default_fields[] = {\n \t\"references.format\",\n-\t\"layout.bare\"\n+\t\"layout.bare\",\n+\t\"layout.shallow\"\n };\n \n static void repo_info_init(struct repo_info *repo_info,\n@@ -81,6 +85,10 @@ static void repo_info_init(struct repo_info *repo_info,\n \t\t\tfield->category = CATEGORY_LAYOUT;\n \t\t\tfield->field.layout = FIELD_LAYOUT_BARE;\n \t\t}\n+\t\telse if (!strcmp(arg, \"layout.shallow\")) {\n+\t\t\tfield->category = CATEGORY_LAYOUT;\n+\t\t\tfield->field.layout = FIELD_LAYOUT_SHALLOW;\n+\t\t}\n \t\telse {\n \t\t\tdie(\"invalid field '%s'\", arg);\n \t\t}\n@@ -110,6 +118,9 @@ static void repo_info_print_plaintext(struct repo_info *repo_info) {\n \t\t\tcase FIELD_LAYOUT_BARE:\n \t\t\t\tputs(is_bare_repository() ? \"true\" : \"false\");\n \t\t\t\tbreak;\n+\t\t\tcase FIELD_LAYOUT_SHALLOW:\n+\t\t\t\tputs(is_repository_shallow(repo) ? \"true\" : \"false\");\n+\t\t\t\tbreak;\n \t\t\t}\n \t\t\tbreak;\n \t\t}\n@@ -158,6 +169,11 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \t\t\tjw_object_bool(&jw, \"bare\",\n \t\t\t\t       is_bare_repository());\n \t\t}\n+\n+\t\tif (layout_fields & FIELD_LAYOUT_SHALLOW) {\n+\t\t\tjw_object_bool(&jw, \"shallow\",\n+\t\t\t\t       is_repository_shallow(repo));\n+\t\t}\n \t\tjw_end(&jw);\n \t}\n \tjw_end(&jw);\ndiff --git a/t/t1518-repo-info.sh b/t/t1518-repo-info.sh\nindex 1831b74551..10de93219b 100755\n--- a/t/t1518-repo-info.sh\n+++ b/t/t1518-repo-info.sh\n@@ -6,7 +6,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n-DEFAULT_NUMBER_OF_FIELDS=2\n+DEFAULT_NUMBER_OF_FIELDS=3\n \n parse_json () {\n \ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n@@ -64,6 +64,51 @@ test_repo_info 'bare repository = true is retrieved correctly' \\\n \t'--bare' \\\n \t'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' \\\n+\t'' \\\n+\t'layout.shallow' 'false'\n+\n+test_expect_success 'json: shallow repository = true is retrieved correctly' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\tcd repo &&\n+\techo x >x &&\n+\tgit add x &&\n+\tgit commit -m x &&\n+\tgit clone --depth 1 \"file://$PWD\" cloned &&\n+\tcd cloned &&\n+\techo 1 >expect &&\n+\tgit repo-info \"layout.shallow\" | parse_json >output &&\n+\tgrep -F \"row[0].layout.shallow\" output | cut -d \" \" -f 2 >actual &&\n+\tcat actual > /dev/ttys001 &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'plaintext: shallow repository = true is retrieved correctly' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\tcd repo &&\n+\techo x >x &&\n+\tgit add x &&\n+\tgit commit -m x &&\n+\ttest_commit \"commit\" &&\n+\tgit clone --depth=1 \"file://$PWD\" cloned &&\n+\tcd cloned &&\n+       \techo true >expect &&\n+       \tgit repo-info --format=plaintext \"layout.shallow\" >actual &&\n+       \ttest_cmp expect actual\n+'\n+\n+test_expect_success 'plaintext: output all default fields' \"\n+\tgit repo-info --format=plaintext >actual &&\n+\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n+\"\n+\n+test_expect_success 'json: output all default fields' \"\n+\tgit repo-info --format=json | parse_json | grep '.*\\..*\\..*' >actual &&\n+\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n+\"\n+\n test_expect_success 'plaintext: output all default fields' \"\n \tgit repo-info --format=plaintext >actual &&\n \ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n@@ -74,4 +119,5 @@ test_expect_success 'json: output all default fields' \"\n \ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n \"\n \n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520049","messageId":"20250610152117.14826-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH 4/5] repo-info: add field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-10T15:21:16Z","receivedAt":"2025-06-10T15:22:07Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the field layout.bare to the repo-info command. The data\nretrieved in this field is the same that currently is obtained by\nrunning `git rev-parse --is-bare-repository`.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 35 ++++++++++++++++++++++++++++++++++-\n t/t1518-repo-info.sh | 12 ++++++++++--\n 2 files changed, 44 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex a1c9d3942e..bc25a0809f 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -1,4 +1,7 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n+#include \"environment.h\"\n #include \"hash.h\"\n #include \"json-writer.h\"\n #include \"parse-options.h\"\n@@ -10,17 +13,22 @@ enum output_format {\n };\n \n enum repo_info_category {\n-\tCATEGORY_REFERENCES = 1\n+\tCATEGORY_REFERENCES = 1,\n+\tCATEGORY_LAYOUT = 1 << 1\n };\n \n enum repo_info_references_field {\n \tFIELD_REFERENCES_FORMAT = 1\n };\n \n+enum repo_info_layout_field { FIELD_LAYOUT_BARE = 1\n+};\n+\n struct repo_info_field {\n \tenum repo_info_category category;\n \tunion {\n \t\tenum repo_info_references_field references;\n+\t\tenum repo_info_layout_field layout;\n \t} field;\n };\n \n@@ -33,6 +41,7 @@ struct repo_info {\n \n const char *default_fields[] = {\n \t\"references.format\",\n+\t\"layout.bare\"\n };\n \n static void repo_info_init(struct repo_info *repo_info,\n@@ -68,6 +77,10 @@ static void repo_info_init(struct repo_info *repo_info,\n \t\t\tfield->category = CATEGORY_REFERENCES;\n \t\t\tfield->field.references = FIELD_REFERENCES_FORMAT;\n \t\t}\n+\t\telse if (!strcmp(arg, \"layout.bare\")) {\n+\t\t\tfield->category = CATEGORY_LAYOUT;\n+\t\t\tfield->field.layout = FIELD_LAYOUT_BARE;\n+\t\t}\n \t\telse {\n \t\t\tdie(\"invalid field '%s'\", arg);\n \t\t}\n@@ -92,6 +105,13 @@ static void repo_info_print_plaintext(struct repo_info *repo_info) {\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\tbreak;\n+\t\tcase CATEGORY_LAYOUT:\n+\t\t\tswitch (field->field.layout) {\n+\t\t\tcase FIELD_LAYOUT_BARE:\n+\t\t\t\tputs(is_bare_repository() ? \"true\" : \"false\");\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n \t\t}\n \t}\n }\n@@ -102,6 +122,7 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \tint i;\n \tunsigned int categories = 0;\n \tunsigned int references_fields = 0;\n+\tunsigned int layout_fields = 0;\n \tstruct repository *repo = repo_info->repo;\n \n \tfor (i = 0; i < repo_info->n_fields; i++) {\n@@ -111,6 +132,9 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \t\tcase CATEGORY_REFERENCES:\n \t\t\treferences_fields |= field->field.references;\n \t\t\tbreak;\n+\t\tcase CATEGORY_LAYOUT:\n+\t\t\tlayout_fields |= field->field.layout;\n+\t\t\tbreak;\n \t\t}\n \t}\n \n@@ -127,6 +151,15 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \t\t}\n \t\tjw_end(&jw);\n \t}\n+\n+\tif (categories & CATEGORY_LAYOUT) {\n+\t\tjw_object_inline_begin_object(&jw, \"layout\");\n+\t\tif (layout_fields & FIELD_LAYOUT_BARE) {\n+\t\t\tjw_object_bool(&jw, \"bare\",\n+\t\t\t\t       is_bare_repository());\n+\t\t}\n+\t\tjw_end(&jw);\n+\t}\n \tjw_end(&jw);\n \n \tputs(jw.json.buf);\ndiff --git a/t/t1518-repo-info.sh b/t/t1518-repo-info.sh\nindex a99198b0f6..1831b74551 100755\n--- a/t/t1518-repo-info.sh\n+++ b/t/t1518-repo-info.sh\n@@ -6,7 +6,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n-DEFAULT_NUMBER_OF_FIELDS=1\n+DEFAULT_NUMBER_OF_FIELDS=2\n \n parse_json () {\n \ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n@@ -22,7 +22,7 @@ test_repo_info () {\n \t\ttest_when_finished 'rm -rf repo' &&\n \t\tgit init $init_args repo &&\n \t\tcd repo &&\n-\t\techo '$expected_value' >expect &&\n+\t\techo '$expected_value' | sed 's/^false$/0/' | sed 's/^true$/1/' >expect &&\n \t\tgit repo-info '$key'| parse_json >output &&\n \t\tgrep -F 'row[0].$key' output | cut -d ' ' -f 2 >actual &&\n \t\ttest_cmp expect actual\n@@ -56,6 +56,14 @@ test_repo_info 'ref format reftable is retrieved correctly' \\\n \t'--ref-format=reftable' \\\n \t'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' \\\n+\t'' \\\n+\t'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' \\\n+\t'--bare' \\\n+\t'layout.bare' 'true'\n+\n test_expect_success 'plaintext: output all default fields' \"\n \tgit repo-info --format=plaintext >actual &&\n \ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520051","messageId":"0e4a7c78-8e64-4c27-8ae2-c2c664dba0a9@app.fastmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-06-10T16:39:09Z","receivedAt":"2025-06-10T16:39:32Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Tue, Jun 10, 2025, at 17:21, Lucas Seiki Oshiro wrote:\n> ## Motivation\n>\n> Currently, `git rev-parse` covers a wide range of functionality not directly\n> related to parsing revisions, as its name says. Over time, many features like\n> parsing datestrings, options, paths, and others were added to it because\n> there wasn't a more appropriated command to place them.\n>\n> This way, many of these features would be better placed in new and dedicated\n> commands. This kind of movement had other precedents in Git, for example, `git\n> switch` and `git restore` were created after `git checkout` became too\n> overloaded.\n\nThis is something I’ve wanted.  Thanks for starting the work.\n\n-- \nKristoffer Haugsbakk\n"},{"id":"520052","messageId":"xmqqikl3mtx2.fsf@gitster.g","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-10T16:40:41Z","receivedAt":"2025-06-10T16:40:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> $ git repo-info\n> {\n>   \"objects\": {\n>     \"format\": \"sha1\"\n>   },\n>   \"references\": {\n>     \"format\": \"files\"\n>   },\n>   \"path\": {\n>     \"git-dir\": \"/git/dir\"\n>     \"git-commom-dir\": \"/git/common-dir\",\n>     \"toplevel\": \"/git/toplevel\",\n>     \"superproject-working-tree\": \"/super/working/tree\",\n>   }\n>   \"layout\": {\n>     \"bare\": false,\n>     \"shallow\": false\n>   }\n> }\n> ~~~\n\nOK, that's understandable, other than that handling of trailing\ncommas looks somewhat inconsistent.\n\n> Or in a plaintext format, like this:\n>\n> ~~~\n> $ git repo-info --format=plaintext\n> sha1\n> files\n> /git/dir\n> /git/common-dir\n> /git/toplevel\n> /super/working/tree\n> false\n> false\n> ~~~\n\nThis one is a bit questionable.\n\nIs it safe to assume that we will never have to deal with payload\ncontrollable by the end-user that can have arbitrary byte values?\nIt would be uncommon but possible for LF to be in one of these\npathnames, breaking one-line-per-item assumption you seem to be\nmaking.  If one-line-per-item can be assumed, you can certainly\nclaim that this format is easy for consumers to handle, but the fact\nis that this format is cumbersome to use safely if we have to deal\nwith end-user controllable arbitrary byte sequences, and if the set\nof info pieces the repo-info needs to deal with changes over time.\n\n> It will also allow the user to get only the desired fields, like this:\n>\n> ~~~\n> $ git repo-info --format=plaintext objects.format references.format\n> sha1\n> files\n> ~~~\n\nIf the user asked for only one item, this is probably OK, but for\nmore than one items, the same comment applies as above (except that\nthe number of and order of info pieces are known in this case).\n\n> or:\n>\n> ~~~\n> $ git repo-info objects.format references.format\n> {\n>   \"objects\": {\n>     \"format\": \"sha1\"\n>   },\n>   \"references\": {\n>     \"format\": \"files\"\n>   }\n> }\n> ~~~\n\nThis one is OK.\n\n> - on deciding if the JSON and linewise plaintext formats are the really the best,\n>   or if I should consider others (e.g. gitconfig syntax, NUL-terminated, etc)\n\n\"git config list\" and \"git config get\" may be weaker in handling\narbitrary bytes than we would wish them to be.  As much as I loath\nJSON, the format is widely recognised and supported, so as the\ninitial cut, it may be sufficient to nail down the schema first and\nthen worry about emitting the same information in other formats\nlater.\n\n\n"},{"id":"520079","messageId":"CAOLa=ZS_JC_3DYUmYu65V7J__yr6Wv=j0Om6JJ47HxMkZSSyJA@mail.gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH 1/5] repo-info: declare the repo-info command","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-11T08:59:02Z","receivedAt":"2025-06-11T08:59:04Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Create a new Git subcommand called repo-info. `git repo-info` will query\n> metadata from the current repository and outputs it as JSON or plaintext.\n>\n> Also add entries for this new command in:\n>\n> - the build files (Makefile and meson.build)\n> - builtin.h\n> - git.c\n> - .gitignore\n>\n\nWe should also add documentation for this new command in\n'Documentation/git-repo-info.adoc'.\n\n[snip]\n"},{"id":"520081","messageId":"CAOLa=ZRFM5ZVyBfbpYjePN1c+cVBTfU-nk+hsQOGt5e1CJ3EXA@mail.gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH 2/5] repo-info: add the --format flag","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-11T09:30:17Z","receivedAt":"2025-06-11T09:30:20Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add the --format flag to the repo-info command, allowing the user to\n> choose betwen the 'json' and 'plaintext' formats as output.\n>\n\ns/betwen/between\n\n> Also add a flag --allow-empty, which will force the output data to be\n> empty when no field is requested.\n>\n\nWhy do you suppose we need this, I'm not against it, but it would be\nnice to state why this is necessary. The idea is to have a default\noutput when a user runs `git repo-info`, so I'm missing why this would\nbe useful.\n\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  builtin/repo-info.c  | 79 ++++++++++++++++++++++++++++++++++++++++++--\n>  t/meson.build        |  1 +\n>  t/t1518-repo-info.sh | 49 +++++++++++++++++++++++++++\n>  3 files changed, 127 insertions(+), 2 deletions(-)\n>  create mode 100755 t/t1518-repo-info.sh\n>\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index 4615b988d8..4d539a17fb 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -1,22 +1,97 @@\n>  #include \"builtin.h\"\n> +#include \"hash.h\"\n\nThis header doesn't seem to be required for compiling this patch.\n\n> +#include \"json-writer.h\"\n>  #include \"parse-options.h\"\n> +#include \"refs.h\"\n\nSimilar, this header too doesn't seem to be required.\n\n> +\n> +enum output_format {\n> +\tFORMAT_PLAINTEXT,\n> +\tFORMAT_JSON\n> +};\n> +\n> +struct repo_info {\n> +\tstruct repository *repo;\n> +\tenum output_format format;\n> +};\n> +\n> +static void repo_info_init(struct repo_info *repo_info,\n> +\t\t\t   struct repository *repo,\n> +\t\t\t   char *format,\n> +\t\t\t   int allow_empty UNUSED,\n\nSo allow_empty isn't even used in this patch, let's separate it out to a\npatch of itself then.\n\n> +\t\t\t   int argc UNUSED,\n> +\t\t\t   const char **argv UNUSED\n> +\t\t\t   ) {\n> +\trepo_info->repo = repo;\n> +\n> +\tif (format == NULL || !strcmp(format, \"json\"))\n> +\t\trepo_info->format = FORMAT_JSON;\n> +\telse if (!strcmp(format, \"plaintext\"))\n> +\t\trepo_info->format = FORMAT_PLAINTEXT;\n> +\telse\n> +\t\tdie(\"invalid format %s\", format);\n> +}\n> +\n> +static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED) {\n> +}\n\nSince we don't implement this in this patch, perhaps we can only\nintroduce the json formatting in this patch? That also allows us to\ncompletely skip the 'plaintext' formatting for now and implement it in\nthe future.\n\n> +static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n> +{\n> +\tstruct json_writer jw;\n> +\n> +\tjw_init(&jw);\n> +\n> +\tjw_object_begin(&jw, 1);\n> +\tjw_end(&jw);\n> +\n> +\tputs(jw.json.buf);\n> +\tjw_release(&jw);\n> +}\n> +\n> +static void repo_info_print(struct repo_info *repo_info)\n> +{\n> +\tenum output_format format = repo_info->format;\n> +\n> +\tswitch (format) {\n> +\tcase FORMAT_PLAINTEXT:\n> +\t\trepo_info_print_plaintext(repo_info);\n> +\t\tbreak;\n> +\tcase FORMAT_JSON:\n> +\t\trepo_info_print_json(repo_info);\n> +\t\tbreak;\n> +\t}\n> +}\n>\n>  int cmd_repo_info(\n>  \tint argc,\n>  \tconst char **argv,\n>  \tconst char *prefix,\n> -\tstruct repository *repo UNUSED\n> +\tstruct repository *repo\n>  \t)\n>  {\n>  \tconst char *const repo_info_usage[] = {\n>  \t\t\"git repo-info\",\n>  \t\tNULL\n>  \t};\n> +\tstruct repo_info repo_info;\n> +\tchar *format = NULL;\n> +\tint allow_empty = 0;\n>  \tstruct option options[] = {\n> +\t\tOPT_STRING(0,\n> +\t\t\t     \"format\",\n> +\t\t\t     &format,\n> +\t\t\t     N_(\"format\"),\n> +\t\t\t     N_(\"output format\")),\n> +\t\tOPT_BOOL(0,\n> +\t\t\t \"allow-empty\",\n> +\t\t\t &allow_empty,\n> +\t\t\t \"when set, it will use an empty set of fields if no field is requested\"),\n>  \t\tOPT_END()\n>  \t};\n>\n> -\targc = parse_options(argc, argv, prefix, options, repo_info_usage, 0);\n> +\targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n> +\t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n\nI think I understand why we use 'PARSE_OPT_KEEP_UNKNOWN_OPT', this is to\nallow users to provide custom fields to print. But this doesn't seem to\nbe mentioned in the commit message or used in this patch, could we do\neither?\n\n[snip]\n\n> diff --git a/t/t1518-repo-info.sh b/t/t1518-repo-info.sh\n> new file mode 100755\n> index 0000000000..2e1a6f0c34\n> --- /dev/null\n> +++ b/t/t1518-repo-info.sh\n> @@ -0,0 +1,49 @@\n> +#!/bin/sh\n> +\n> +test_description='test git repo-info'\n> +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> +\n> +. ./test-lib.sh\n> +\n> +parse_json () {\n> +\ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n> +}\n> +\n\nIf I recall correctly, in our previous internal review, it was mentioned\nthat since we use perl here, we should add a PERL pre-requisite. Similar\nto the one in 't/t0019-json-writer.sh'.\n\n> +test_repo_info () {\n\nNit: would be nice to have some documentation on this function,\nregarding:\n1. Its purpose and use case\n2. The fields and what they mean\n\nYou can take some hints from 'test_migration' in\n't/t1460-refs-migrate.sh'.\n\n> +\tlabel=$1\n> +\tinit_args=$2\n> +\tkey=$3\n> +\texpected_value=$4\n> +\n> +\ttest_expect_success \"json: $label\" \"\n> +\t\ttest_when_finished 'rm -rf repo' &&\n> +\t\tgit init $init_args repo &&\n> +\t\tcd repo &&\n> +\t\techo '$expected_value' >expect &&\n> +\t\tgit repo-info '$key'| parse_json >output &&\n> +\t\tgrep -F 'row[0].$key' output | cut -d ' ' -f 2 >actual &&\n> +\t\ttest_cmp expect actual\n> +\t\"\n> +\n> +\ttest_expect_success \"plaintext: $label\" \"\n> +\t\ttest_when_finished 'rm -rf repo' &&\n> +\t\tgit init $init_args repo &&\n> +\t\tcd repo &&\n> +\t\techo '$expected_value' >expect &&\n> +\t\tgit repo-info --format=plaintext '$key' >actual &&\n> +\t\ttest_cmp expect actual\n> +\t\"\n> +}\n> +\n> +test_expect_success 'json: returns empty output with allow-empty' '\n> +\tgit repo-info --allow-empty --format=json >output &&\n> +\ttest_line_count = 2 output\n> +'\n> +\n\nAs of this patch,\n\n  $ ~/code/git/build/bin-wrappers/git repo-info\n  {\n  }\n\n  $ ~/code/git/build/bin-wrappers/git repo-info --allow-empty\n  {\n  }\n\nSo what differentiates the former from the latter?\n\n> +test_expect_success 'plaintext: returns empty output with allow-empty' '\n> +\tgit repo-info --allow-empty --format=plaintext >output &&\n> +\ttest_line_count = 0 output\n> +'\n>\n\nThis is because we didn't implement plainttext no?\n\n> +\n> +test_done\n> --\n> 2.39.5 (Apple Git-154)\n\nOverall, I think the changes here make sense to me. But the commit\ncan be broken up and divided into individual bits:\n1. Commit to introduce the --format=json flag\n2. Commit to introduce the --format=plaintext flag (can be done later as\n   needed)\n3. Commit to add '--allow-empty' flag (is this needed?).\n\nThis way the tests and code can also be split as per these commits, this\nwould make reviews much easier since the current patch has some dead\ncode which presumably will be used in upcoming patches.\n\n- Karthik\n"},{"id":"520086","messageId":"CAOLa=ZTNpSKoFcfQHDOQNhq-jdFJvGEsXm-rYKrHCZG8CfaqaQ@mail.gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-4-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH 3/5] repo-info: add the field references.format","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-11T12:59:32Z","receivedAt":"2025-06-11T12:59:34Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add the field references.format to the repo-info command. The data\n> retrieved in this field is the same that currently is obtained by\n> running `git rev-parse --show-ref-format`.\n>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  builtin/repo-info.c  | 97 +++++++++++++++++++++++++++++++++++++++++---\n>  t/t1518-repo-info.sh | 20 +++++++++\n>  2 files changed, 112 insertions(+), 5 deletions(-)\n>\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index 4d539a17fb..a1c9d3942e 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -9,18 +9,40 @@ enum output_format {\n>  \tFORMAT_JSON\n>  };\n>\n> +enum repo_info_category {\n> +\tCATEGORY_REFERENCES = 1\n> +};\n> +\n> +enum repo_info_references_field {\n> +\tFIELD_REFERENCES_FORMAT = 1\n> +};\n> +\n> +struct repo_info_field {\n> +\tenum repo_info_category category;\n> +\tunion {\n> +\t\tenum repo_info_references_field references;\n> +\t} field;\n> +};\n> +\n>  struct repo_info {\n>  \tstruct repository *repo;\n>  \tenum output_format format;\n> +\tint n_fields;\n> +\tstruct repo_info_field *fields;\n> +};\n> +\n> +const char *default_fields[] = {\n> +\t\"references.format\",\n>  };\n>\n>  static void repo_info_init(struct repo_info *repo_info,\n>  \t\t\t   struct repository *repo,\n>  \t\t\t   char *format,\n> -\t\t\t   int allow_empty UNUSED,\n> -\t\t\t   int argc UNUSED,\n> -\t\t\t   const char **argv UNUSED\n> +\t\t\t   int allow_empty,\n> +\t\t\t   int argc,\n> +\t\t\t   const char **argv\n>  \t\t\t   ) {\n>\n\nNit: we wrap to 80 chars generally, so you can put multiple arguments on\nthe same line.\n\n> +\tint i;\n>  \trepo_info->repo = repo;\n>\n>  \tif (format == NULL || !strcmp(format, \"json\"))\n> @@ -29,18 +51,82 @@ static void repo_info_init(struct repo_info *repo_info,\n>  \t\trepo_info->format = FORMAT_PLAINTEXT;\n>  \telse\n>  \t\tdie(\"invalid format %s\", format);\n> +\n> +\tif (argc == 0 && !allow_empty) {\n> +\t\targc = ARRAY_SIZE(default_fields);\n> +\t\targv = default_fields;\n> +\t}\n> +\n> +\trepo_info->n_fields = argc;\n> +\trepo_info->fields = xmalloc(argc * sizeof(struct repo_info_field));\n> +\n\nNit: perhaps use ALLOC_ARRAY or family here?\n\n> +\tfor (i = 0; i < argc; i++) {\n> +\t\tconst char *arg = argv[i];\n> +\t\tstruct repo_info_field *field = repo_info->fields + i;\n> +\n> +\t\tif (!strcmp(arg, \"references.format\")) {\n> +\t\t\tfield->category = CATEGORY_REFERENCES;\n> +\t\t\tfield->field.references = FIELD_REFERENCES_FORMAT;\n> +\t\t}\n\nMakes me wonder if the default fields can be defined as an array of\n'repo_info_field' and avoid the strcmp since the information is\npre-defined. Perhaps something like:\n\n  diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n  index a1c9d3942e..81c7b5f896 100644\n  --- a/builtin/repo-info.c\n  +++ b/builtin/repo-info.c\n  @@ -31,8 +31,13 @@ struct repo_info {\n   \tstruct repo_info_field *fields;\n   };\n\n  -const char *default_fields[] = {\n  -\t\"references.format\",\n  +const struct repo_info_field default_fields[] = {\n  +\t{\n  +\t\t.category = CATEGORY_REFERENCES,\n  +\t\t.field = {\n  +\t\t\t.references = FIELD_REFERENCES_FORMAT\n  +\t\t},\n  +\t},\n   };\n\n   static void repo_info_init(struct repo_info *repo_info,\n  @@ -53,8 +58,8 @@ static void repo_info_init(struct repo_info *repo_info,\n   \t\tdie(\"invalid format %s\", format);\n\n   \tif (argc == 0 && !allow_empty) {\n  -\t\targc = ARRAY_SIZE(default_fields);\n  -\t\targv = default_fields;\n  +\t\trepo_info->fields = (struct repo_info_field *)&default_fields;\n  +\t\treturn;\n   \t}\n\n   \trepo_info->n_fields = argc;\n\n\n> +\t\telse {\n> +\t\t\tdie(\"invalid field '%s'\", arg);\n> +\t\t}\n> +\t}\n> +}\n> +\n> +static void repo_info_release(struct repo_info *repo_info) {\n> +\tfree(repo_info->fields);\n>  }\n>\n> -static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED) {\n> +static void repo_info_print_plaintext(struct repo_info *repo_info) {\n\nThis should definitely go into its own commit or at least be mentioned in\nthe commit message.\n\n> +\tstruct repository *repo = repo_info->repo;\n> +\tint i;\n\nNit: always nice to leave a newline between the variable declarations\nand following code. Also we can move move `int i` declaration directly\ninside the loop.\n\n> +\tfor (i = 0; i < repo_info->n_fields; i++) {\n> +\t\tstruct repo_info_field *field = &repo_info->fields[i];\n> +\t\tswitch (field->category) {\n> +\t\tcase CATEGORY_REFERENCES:\n> +\t\t\tswitch (field->field.references) {\n> +\t\t\tcase FIELD_REFERENCES_FORMAT:\n> +\t\t\t\tputs(ref_storage_format_to_name(\n> +\t\t\t\t\trepo->ref_storage_format));\n> +\t\t\t\tbreak;\n> +\t\t\t}\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n>  }\n>\n> -static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n> +static void repo_info_print_json(struct repo_info *repo_info)\n>  {\n>  \tstruct json_writer jw;\n> +\tint i;\n> +\tunsigned int categories = 0;\n> +\tunsigned int references_fields = 0;\n> +\tstruct repository *repo = repo_info->repo;\n> +\n> +\tfor (i = 0; i < repo_info->n_fields; i++) {\n> +\t\tstruct repo_info_field *field = repo_info->fields + i;\n> +\t\tcategories |= field->category;\n> +\t\tswitch (field->category) {\n> +\t\tcase CATEGORY_REFERENCES:\n> +\t\t\treferences_fields |= field->field.references;\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n>\n>  \tjw_init(&jw);\n>\n>  \tjw_object_begin(&jw, 1);\n> +\n> +\tif (categories & CATEGORY_REFERENCES) {\n> +\t\tjw_object_inline_begin_object(&jw, \"references\");\n> +\t\tif (references_fields & FIELD_REFERENCES_FORMAT) {\n> +\t\t\tconst char *format_name = ref_storage_format_to_name(\n> +\t\t\t\trepo->ref_storage_format);\n> +\t\t\tjw_object_string(&jw, \"format\", format_name);\n> +\t\t}\n> +\t\tjw_end(&jw);\n> +\t}\n>  \tjw_end(&jw);\n>\n\nDoesn't this mean that each value of CATEGORY_REFERENCES and\nFIELD_REFERENCES_FORMAT should have no common bits? Especially in the\nformer across different categories too.\n\nI wonder if we can solve this in a easier way. We care about:\n- Retaining the order of fields input by the user\n- Having a default set of fields if no input received\n\nBut, for JSON formatting, order of fields doesn't matter as per the spec\n[1]:\n\n  An object is an unordered set of name/value pairs. An object begins\n  with {left brace and ends with }right brace. Each name is followed by\n  :colon and the name/value pairs are separated by ,comma.\n\nSo, do we really want to keep the order? Especially since one suggestion\nis to only work with the JSON format for now.\n\nIf we don't care about the order, we can simply have a structure with\nbit fields and work with that.\n\n>  \tputs(jw.json.buf);\n> @@ -92,6 +178,7 @@ int cmd_repo_info(\n>  \t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n>  \trepo_info_init(&repo_info, repo, format, allow_empty, argc, argv);\n>  \trepo_info_print(&repo_info);\n> +\trepo_info_release(&repo_info);\n>\n>  \treturn 0;\n>  }\n> diff --git a/t/t1518-repo-info.sh b/t/t1518-repo-info.sh\n> index 2e1a6f0c34..a99198b0f6 100755\n> --- a/t/t1518-repo-info.sh\n> +++ b/t/t1518-repo-info.sh\n> @@ -6,6 +6,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>\n>  . ./test-lib.sh\n>\n> +DEFAULT_NUMBER_OF_FIELDS=1\n> +\n>  parse_json () {\n>  \ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n>  }\n> @@ -46,4 +48,22 @@ test_expect_success 'plaintext: returns empty output with allow-empty' '\n>  \ttest_line_count = 0 output\n>  '\n>\n> +test_repo_info 'ref format files is retrieved correctly' \\\n> +\t'' \\\n> +\t'references.format' 'files'\n> +\n\nThis expects that the repository is created with the 'files' backend by\ndefault, but that is defined by the 'GIT_TEST_DEFAULT_REF_FORMAT' env\nvariable. So wouldn't this fail when in the CI job for reftables?\n\n> +test_repo_info 'ref format reftable is retrieved correctly' \\\n> +\t'--ref-format=reftable' \\\n> +\t'references.format' 'reftable'\n> +\n> +test_expect_success 'plaintext: output all default fields' \"\n> +\tgit repo-info --format=plaintext >actual &&\n> +\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n> +\"\n> +\n> +test_expect_success 'json: output all default fields' \"\n> +\tgit repo-info --format=json | parse_json | grep '.*\\..*\\..*' >actual &&\n> +\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n> +\"\n> +\n\nWhile line count is good, we should also check the default values, no?\n\n>  test_done\n> --\n> 2.39.5 (Apple Git-154)\n"},{"id":"520088","messageId":"CAOLa=ZTvC7Hq5Fz6N1ESzf1s=7kkyZG5iYTA6pzxnKm2p4oMNw@mail.gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-5-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH 4/5] repo-info: add field layout.bare","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-11T13:13:34Z","receivedAt":"2025-06-11T13:13:38Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add the field layout.bare to the repo-info command. The data\n> retrieved in this field is the same that currently is obtained by\n> running `git rev-parse --is-bare-repository`.\n>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  builtin/repo-info.c  | 35 ++++++++++++++++++++++++++++++++++-\n>  t/t1518-repo-info.sh | 12 ++++++++++--\n>  2 files changed, 44 insertions(+), 3 deletions(-)\n>\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index a1c9d3942e..bc25a0809f 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -1,4 +1,7 @@\n> +#define USE_THE_REPOSITORY_VARIABLE\n> +\n\nAh! Seems like `is_bare_repository()` is responsible for this, it would\nbe nice to not introduce global dependency in a new command, but this\nisn't part of your project, so it's okay here.\n\n>  #include \"builtin.h\"\n> +#include \"environment.h\"\n>  #include \"hash.h\"\n>  #include \"json-writer.h\"\n>  #include \"parse-options.h\"\n> @@ -10,17 +13,22 @@ enum output_format {\n>  };\n>\n>  enum repo_info_category {\n> -\tCATEGORY_REFERENCES = 1\n> +\tCATEGORY_REFERENCES = 1,\n> +\tCATEGORY_LAYOUT = 1 << 1\n\nStyle: If we're doing this, then it also makes sense to use `1 << 0`.\n\n>  };\n>\n>  enum repo_info_references_field {\n>  \tFIELD_REFERENCES_FORMAT = 1\n>  };\n>\n> +enum repo_info_layout_field { FIELD_LAYOUT_BARE = 1\n> +};\n> +\n\nStyle: let's keep the styling consistent with the previous block.\n\n[snip]\n"},{"id":"520089","messageId":"CAOLa=ZQrJ06Se1+kZjCa2Mkhv7Q5gh8ZgtLh0usqZqTBPcVxSg@mail.gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-11T13:17:44Z","receivedAt":"2025-06-11T13:17:46Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n[snip]\n\n> ## Feedback\n>\n> I would like to ask for your feedback on this proprosal, specially:\n>\n> - on deciding if the JSON and linewise plaintext formats are the really the best,\n>   or if I should consider others (e.g. gitconfig syntax, NUL-terminated, etc)\n>\n> - on deciding how the fields will be specified. This \"<category>.<format>\" was\n>   a first idea based on the JSON structure\n>\n> - about the JSON schema\n>\n> - about information that may be nice to include in the output of this command,\n>   even if they are not currently retrieved by `rev-parse`\n>\n\nOne thing I'd also like to see is some thought/documentation of what\nkind of information should go into this command and what shouldn't. For\ne.g. should number of references in a repo be part of this command, or\nthe `git refs` command. More broadly, which subsystem information should\nbe exposed via `git repo-info` and how do we draw that line.\n\nMostly to not create a new `git rev-parse` alternative.\n\n> Thanks!\n>\n\nI've also gone ahead and reviewed the patches individually and left some\ncomments.\n\n- Karthik\n"},{"id":"520172","messageId":"xmqqecvpf1hk.fsf@gitster.g","threadId":"63618","inReplyTo":"CAOLa=ZTNpSKoFcfQHDOQNhq-jdFJvGEsXm-rYKrHCZG8CfaqaQ@mail.gmail.com","subject":"Re: [GSoC RFC PATCH 3/5] repo-info: add the field references.format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-12T15:01:11Z","receivedAt":"2025-06-12T15:01:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n>>  static void repo_info_init(struct repo_info *repo_info,\n>>  \t\t\t   struct repository *repo,\n>>  \t\t\t   char *format,\n>> -\t\t\t   int allow_empty UNUSED,\n>> -\t\t\t   int argc UNUSED,\n>> -\t\t\t   const char **argv UNUSED\n>> +\t\t\t   int allow_empty,\n>> +\t\t\t   int argc,\n>> +\t\t\t   const char **argv\n>>  \t\t\t   ) {\n>>\n>\n> Nit: we wrap to 80 chars generally, so you can put multiple arguments on\n> the same line.\n\nGood point.  It is worth pointing out that we also group related\narguments together, and make it easier to later add new things at\nthe end and still keep related things together, e.g.,\n\nstatic void repo_info_init(struct repo_info *repo_info,\n\t\t\t   struct repository *repo,\n                           int argc, const char **argv,\n\t\t\t   const char *format,\n\t\t\t   int allow_empty)\n{\n\nObviously argc, argv belong to each other, so it is OK to have on\nthe same line, and unlike repo_info (i.e. the out argument), repo\n(i.e. the primary thing that is inspected), format and allow_empty\nare something you would want to later extend when you \"enrich\" the\ninterface and functionality.  You may even gain \"int display_width\"\nargument to allow line-wrapped output, for example, and you would\nhave to insert in the middle if you want to keep related things\ntogether, if you had (argc, argv) at the end. \n\nAlso note that {opening and closing braces} around the function body\nsits on their own lines.\n\nWhy is \"format\" a string?  Shouldn't the caller do all the argument\nparsing and pass an enum or something to this function?  After all,\nI presume that allow_empty is set in response to \"--allow-empty\" or\nsomething that the user gave to the command, and that parsing is done\nby the caller before it calls this function, no?  Why not do the\nsame for the output format?\n\nI'll stop here for now.\n"},{"id":"520181","messageId":"5115663C-0F90-49A2-9925-389715618580@gmail.com","threadId":"63618","inReplyTo":"CAOLa=ZRFM5ZVyBfbpYjePN1c+cVBTfU-nk+hsQOGt5e1CJ3EXA@mail.gmail.com","subject":"Re: [GSoC RFC PATCH 2/5] repo-info: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-12T17:56:05Z","receivedAt":"2025-06-12T17:56:20Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":">> Also add a flag --allow-empty, which will force the output data to be\n>> empty when no field is requested.\n>> \n> Why do you suppose we need this, I'm not against it, but it would be\n> nice to state why this is necessary. The idea is to have a default\n> output when a user runs `git repo-info`, so I'm missing why this would\n> be useful.\n\nI was thinking about use cases where repo-info is used inside scripts.\nA simple (but kinda useless...) example: an application that is a GUI\nfor this command, where the fields are selected in a checkbox, calling\nrepo-info with them and then displaying their contents in a dialog.\n\nIn this example, if no field is selected and there's no validation in\nthe GUI side, the default set of data will be retrieved. With\ngit repo-info --allow-empty, we don't need to care about it.\n\nBut, yeah, I agree that we don't need to think about it by now...\n\n> If I recall correctly, in our previous internal review, it was mentioned\n> that since we use perl here, we should add a PERL pre-requisite. Similar\n> to the one in 't/t0019-json-writer.sh'.\n\nDo you mean to use a `test_lazy_prereq`? I'll take a look on that.\n\n> As of this patch,\n> \n>  $ ~/code/git/build/bin-wrappers/git repo-info\n>  {\n>  }\n> \n>  $ ~/code/git/build/bin-wrappers/git repo-info --allow-empty\n>  {\n>  }\n> \n> So what differentiates the former from the latter?\n\nIn this patch, nothing. I was only trying to keep the consistency\nof each patch from 3 to 5 implementing only the code related to the\nfield, but it seems ok to me to squash this patch and the next one.\n\n> This is because we didn't implement plainttext no?\n\nYes!\n\n"},{"id":"520182","messageId":"CF0AD6B1-80D8-417D-9DFD-32338D9EC92A@gmail.com","threadId":"63618","inReplyTo":"CAOLa=ZTvC7Hq5Fz6N1ESzf1s=7kkyZG5iYTA6pzxnKm2p4oMNw@mail.gmail.com","subject":"Re: [GSoC RFC PATCH 4/5] repo-info: add field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-12T19:39:36Z","receivedAt":"2025-06-12T19:39:51Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n>> +#define USE_THE_REPOSITORY_VARIABLE\n>> \n> Ah! Seems like `is_bare_repository()` is responsible for this, it would\n> be nice to not introduce global dependency in a new command, but this\n> isn't part of your project, so it's okay here.\n\nYeah, to be honest I was reluctant to use this, and I tried to find if\nI could easily drop this dependency. But this is `is_bare_repository`:\n\nint is_bare_repository(void)\n{\n\t/* if core.bare is not 'false', let's see if there is a work tree */\n\treturn is_bare_repository_cfg && !repo_get_work_tree(the_repository);\n}\n\nBut I couldn't find out what is the dependency of is_bare_repository_cfg\non the_repository yet, but I decided to keep for this RFC.\n\n"},{"id":"520183","messageId":"xmqq7c1genyp.fsf@gitster.g","threadId":"63618","inReplyTo":"CF0AD6B1-80D8-417D-9DFD-32338D9EC92A@gmail.com","subject":"Re: [GSoC RFC PATCH 4/5] repo-info: add field layout.bare","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-12T19:53:18Z","receivedAt":"2025-06-12T19:53:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n>>> +#define USE_THE_REPOSITORY_VARIABLE\n>>> \n>> Ah! Seems like `is_bare_repository()` is responsible for this, it would\n>> be nice to not introduce global dependency in a new command, but this\n>> isn't part of your project, so it's okay here.\n>\n> Yeah, to be honest I was reluctant to use this, and I tried to find if\n> I could easily drop this dependency. But this is `is_bare_repository`:\n>\n> int is_bare_repository(void)\n> {\n> \t/* if core.bare is not 'false', let's see if there is a work tree */\n> \treturn is_bare_repository_cfg && !repo_get_work_tree(the_repository);\n> }\n>\n> But I couldn't find out what is the dependency of is_bare_repository_cfg\n> on the_repository yet, but I decided to keep for this RFC.\n\nI suspect that by the time setup_git_env() is called in the startup\nsequence from setup_git_directory(), we know that the repository\nknows if the repository is bare.  So one thing we could do is to add\nis-bare-repository-cfg bit as a new member to the repo-settings\nobject of the_repository and record the bit before the\nsetup_git_directory() callchain returns.\n\nThen you can teach is_bare_repository() to take a repo object from\nits caller and the above may become something like\n\n\tint repository_is_bare(struct repository *r)\n\t{\n\t\treturn (r->settings.is_bare_repository &&\n\t\t\t!repo_get_work_tree(r));\n\t}\n\nperhaps.\n"},{"id":"520189","messageId":"2EB72983-BA77-47C3-9331-B08760B092C4@gmail.com","threadId":"63618","inReplyTo":"xmqqikl3mtx2.fsf@gitster.g","subject":"Re: [GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-12T20:25:22Z","receivedAt":"2025-06-12T20:25:27Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> OK, that's understandable, other than that handling of trailing\n> commas looks somewhat inconsistent.\n\nOoops, it was typo. I'm using the json_writer functions\n\n>> Or in a plaintext format, like this:\n>> \n>> ~~~\n>> $ git repo-info --format=plaintext\n>> sha1\n>> files\n>> /git/dir\n>> /git/common-dir\n>> /git/toplevel\n>> /super/working/tree\n>> false\n>> false\n>> ~~~\n> \n> This one is a bit questionable.\n> \n> Is it safe to assume that we will never have to deal with payload\n> controllable by the end-user that can have arbitrary byte values?\n\nI was trying to follow the behavior of rev-parse, where we can\ndo this:\n\ngit rev-parse --show-toplevel --is-bare-repository\n\nand it prints both the top level repositority and whether it is\na bare repository.\n\n> It would be uncommon but possible for LF to be in one of these\n> pathnames\n\nI tested here and this also happened with rev-parse:\n\n$ git init 'my\n  repo'\n\n$ cd my\\nrepo/\n$ git rev-parse --show-toplevel\n/tmp/my\nrepo\n\nAnd this also happens passing other flags to rev-parse...\n\n> This one is OK.\n\nSo, after reading your review, I though about other solution:\n\n1. The user can provide only one field or no field\n2. If the user provide only one field, repo-info will return\n   only its contents\n3. If the user don't provide any field, the default set of\n   fields will be returned\n"},{"id":"520192","messageId":"xmqq4iwkd68p.fsf@gitster.g","threadId":"63618","inReplyTo":"2EB72983-BA77-47C3-9331-B08760B092C4@gmail.com","subject":"Re: [GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-12T21:01:26Z","receivedAt":"2025-06-12T21:01:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> I was trying to follow the behavior of rev-parse, where we can\n> do this:\n>\n> git rev-parse --show-toplevel --is-bare-repository\n\nAs often said, an earlier mistake is not an excuse to pile more of\nthem on top.  Isn't the whole point of this new command to remove\nthese kitchen-sink options out of rev-parse and give them better\nhome?  Let's learn from our earlier mistakes and do it right in the\nnew incarnation.\n\n> So, after reading your review, I though about other solution:\n>\n> 1. The user can provide only one field or no field\n> 2. If the user provide only one field, repo-info will return\n>    only its contents\n> 3. If the user don't provide any field, the default set of\n>    fields will be returned\n\nHmm.  Isn't\n\n4. The output always comes out in JSON.  There should be plenty JSON\n   reading libraries available to the GUI command or whatever that\n   forks this command and reads from it, right?\n\nan option?  If we want textual format, and assume that almost all\nvalues are text, we could do something like\n\n 1. In text mode, the values are shown one-item-per-line, even if\n    the value has embedded LF in it.  At least we assume that the\n    values do not contain a NUL byte.\n\n 2. If a value does not have LF or double-quote in it, it is output\n    literally.\n\n 3. Otherwise, the value is shown with quote_c_style().\n\nThe implication of which is that a loosely written program that does\nnot grok funny values (namely, a string that contains a double-quote\nor a line-feed) can be written loosely without having to worry about\nquoting and assume one-line-per-item.  They may show a wrong value,\nbut at least because one-line-per-item assumption holds, their\ninput/request and value they receive from the program will not go\nout of sync.  And a more carefully written program can of course\ncheck the first byte to see if it is a double-quote, in which case\nthey have to unquote the whole line (which may yield a multi-line\nvalue if the quoted string had embedded \"\\n\"s).\n"},{"id":"520209","messageId":"CAOLa=ZQtZy_xwn5JWKB39maXJ7zSaO_SJQx6UCaomW+X+s_dPA@mail.gmail.com","threadId":"63618","inReplyTo":"5115663C-0F90-49A2-9925-389715618580@gmail.com","subject":"Re: [GSoC RFC PATCH 2/5] repo-info: add the --format flag","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-13T07:31:18Z","receivedAt":"2025-06-13T07:31:20Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n>>> Also add a flag --allow-empty, which will force the output data to be\n>>> empty when no field is requested.\n>>>\n>> Why do you suppose we need this, I'm not against it, but it would be\n>> nice to state why this is necessary. The idea is to have a default\n>> output when a user runs `git repo-info`, so I'm missing why this would\n>> be useful.\n>\n> I was thinking about use cases where repo-info is used inside scripts.\n> A simple (but kinda useless...) example: an application that is a GUI\n> for this command, where the fields are selected in a checkbox, calling\n> repo-info with them and then displaying their contents in a dialog.\n>\n> In this example, if no field is selected and there's no validation in\n> the GUI side, the default set of data will be retrieved. With\n> git repo-info --allow-empty, we don't need to care about it.\n>\n> But, yeah, I agree that we don't need to think about it by now...\n>\n>> If I recall correctly, in our previous internal review, it was mentioned\n>> that since we use perl here, we should add a PERL pre-requisite. Similar\n>> to the one in 't/t0019-json-writer.sh'.\n>\n> Do you mean to use a `test_lazy_prereq`? I'll take a look on that.\n>\n\nYes, that's the one.\n\n>> As of this patch,\n>>\n>>  $ ~/code/git/build/bin-wrappers/git repo-info\n>>  {\n>>  }\n>>\n>>  $ ~/code/git/build/bin-wrappers/git repo-info --allow-empty\n>>  {\n>>  }\n>>\n>> So what differentiates the former from the latter?\n>\n> In this patch, nothing. I was only trying to keep the consistency\n> of each patch from 3 to 5 implementing only the code related to the\n> field, but it seems ok to me to squash this patch and the next one.\n>\n>> This is because we didn't implement plainttext no?\n>\n> Yes!\n"},{"id":"520298","messageId":"C339DA52-F88A-4BC6-98DD-1586A1B4A175@gmail.com","threadId":"63618","inReplyTo":"xmqq4iwkd68p.fsf@gitster.g","subject":"Re: [GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-16T22:19:23Z","receivedAt":"2025-06-16T22:19:37Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Let's learn from our earlier mistakes and do it right in the\n> new incarnation.\n\nOk, fair!\n\n> 1. In text mode, the values are shown one-item-per-line, even if\n>    the value has embedded LF in it.  At least we assume that the\n>    values do not contain a NUL byte.\n> \n> 2. If a value does not have LF or double-quote in it, it is output\n>    literally.\n> \n> 3. Otherwise, the value is shown with quote_c_style().\n\nHmmm, ok. So, I if I understood it correctly, is your idea to keep\nthe same functionality but using quote_c_style to keep one field\nper line?\n\n> The implication of which is that a loosely written program that does\n> not grok funny values (namely, a string that contains a double-quote\n> or a line-feed) can be written loosely without having to worry about\n> quoting and assume one-line-per-item.  They may show a wrong value,\n> but at least because one-line-per-item assumption holds, their\n> input/request and value they receive from the program will not go\n> out of sync.  And a more carefully written program can of course\n> check the first byte to see if it is a double-quote, in which case\n> they have to unquote the whole line (which may yield a multi-line\n> value if the quoted string had embedded \"\\n\"s).\n\nThanks again, Junio! \n"},{"id":"520301","messageId":"xmqqfrfzz4xq.fsf@gitster.g","threadId":"63618","inReplyTo":"C339DA52-F88A-4BC6-98DD-1586A1B4A175@gmail.com","subject":"Re: [GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-16T22:40:01Z","receivedAt":"2025-06-16T22:40:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n>> Let's learn from our earlier mistakes and do it right in the\n>> new incarnation.\n>\n> Ok, fair!\n>\n>> 1. In text mode, the values are shown one-item-per-line, even if\n>>    the value has embedded LF in it.  At least we assume that the\n>>    values do not contain a NUL byte.\n>> \n>> 2. If a value does not have LF or double-quote in it, it is output\n>>    literally.\n>> \n>> 3. Otherwise, the value is shown with quote_c_style().\n>\n> Hmmm, ok. So, I if I understood it correctly, is your idea to keep\n> the same functionality but using quote_c_style to keep one field\n> per line?\n\nI am not sure what you are comparing against with \"the same\" here,\nbut the idea is that not many items have problematic characters in\nthem anyway, and a simple-minded broken output parser can be written\nin 5 minutes and would happen to work correctly in more than 80% of\nrepositories ;-)  A production quality tool needs to be aware of the\npossibility that they may need to unquote when they see a double-quote\nat the beginning of a line.\n\nBy the way, somebody wondered in a nearby message why we do not have\nthe \"key\" and only \"value\" in the textual output, and I share the\nsame sentiment.  Where JSON may say\n\n  \"path\": {\n    \"git-dir\": \"/git/dir\"\n    ...\n\ntext format may want to say\n\n  path.git-dir=/git/dir\n\ninstead of just\n\n  /git/dir\n\nBut I dunno.  When asking a single item from the command line, it is\nperfectly fine to respond with a single value, but when you are\nshowing multiple items (especially without any \"give me this and\nthat item and nothing else\" specified on the command line, in which\ncase nobody knows in what order the value is given), I think the\ncalling script almost always wants to get key-value pairs.\n\nAlso are you planning on later adding a long-running server mode\nlike \"cat-file --batch-command\"?\n"},{"id":"520393","messageId":"110154E5-666B-4093-ABB0-ABAF2E3E107D@gmail.com","threadId":"63618","inReplyTo":"xmqqfrfzz4xq.fsf@gitster.g","subject":"Re: [GSoC RFC PATCH 0/5] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-19T01:44:34Z","receivedAt":"2025-06-19T01:44:52Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> But I dunno.  When asking a single item from the command line, it is\n> perfectly fine to respond with a single value, but when you are\n> showing multiple items (especially without any \"give me this and\n> that item and nothing else\" specified on the command line, in which\n> case nobody knows in what order the value is given), I think the\n> calling script almost always wants to get key-value pairs.\n\nOk! I'm polishing my v2 and I expect to sent it soon. I already changed the\noutput to that format.\n\n> Also are you planning on later adding a long-running server mode\n> like \"cat-file --batch-command\"?\n\nTo be honest, I didn't know about that feature of cat-file until your comment.\nIt seems to be a nice feature to add, entering the keys and printing the\ncorresponding values.\n\nI'm adding a --batch-command feature and the removal of the dependency on\nthe_repository to a todo list to be considered in a future iteration on this\ncommand, after delivering its basic functionality.\n\nThanks!"},{"id":"520427","messageId":"20250619225751.99699-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v2 0/7] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-19T22:57:44Z","receivedAt":"2025-06-19T22:58:02Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\nThis is the second version of the repo-info RFC, and these are the main changes\nintroduced since v1:\n\n- The plaintext format now returns its fields in a key=value format\n\n- The tests were renumbered to t1900, since it's a new command (the previous was\n  t1518, following the numbering of rev-parse)\n\n- The test function 'test_repo_info' now has a docstring, and it is more flexible\n  for using more complex repository initializations\n\n- The flag --allow-empty is now introduced in its own commit\n\n- The plaintext and the JSON formats are now introduced in their own commits\n\n- The JSON format tests, which depends on the Perl's JSON module, are now marked\n  with the PERLJSON lazy prereq, being skipped in environments that don't have\n  that module installed\n\nSome things pointed in the last review weren't implemented as I prefer to do\nthem in another iteration of repo-info after having its basic functionality\nworking:\n\n- Remove the dependency on 'the_repository' when calling 'is_bare_repository'\n\n- Add a --batch-command mode, based on the --bath-command flag of cat-file,\n  introduced in 440c705ea6 (cat-file: add --batch-command mode, 2022-02-18)\n\n- Use the category as key instead of only accepting category.key. In the current\n  patchset, `git repo-info layout` would equivalent to\n  `git repo-info layout.bare layout.shallow`\n\nI'm cc'ing my mentors and everyone that answered me in v1 and my GSoC blog. I'm\nstill open to any suggestions, requests and questions that you may have!\n\nHere's the range-diff of this v2:\n\n1:  20a3d131c3 ! 1:  102b5ce90a repo-info: declare the repo-info command\n    @@ Commit message\n         - git.c\n         - .gitignore\n\n    +    In option parsing, use PARSE_OPT_KEEP_UNKNOWN_OPT to allow the users\n    +    specify after the flags the information that they want to retrieve.\n    +\n         Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n         Mentored-by Patrick Steinhardt <ps@pks.im>\n         Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n    @@ builtin/repo-info.c (new)\n     +#include \"builtin.h\"\n     +#include \"parse-options.h\"\n     +\n    -+int cmd_repo_info(\n    -+\tint argc,\n    -+\tconst char **argv,\n    -+\tconst char *prefix,\n    -+\tstruct repository *repo UNUSED\n    -+\t)\n    ++int cmd_repo_info(int argc,\n    ++\t\t  const char **argv,\n    ++\t\t  const char *prefix,\n    ++\t\t  struct repository *repo UNUSED)\n     +{\n     +\tconst char *const repo_info_usage[] = {\n     +\t\t\"git repo-info\",\n    @@ builtin/repo-info.c (new)\n     +\t\tOPT_END()\n     +\t};\n     +\n    -+\targc = parse_options(argc, argv, prefix, options, repo_info_usage, 0);\n    ++\targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n    ++\t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n     +\n     +\treturn 0;\n     +}\n2:  f78058c25a < -:  ---------- repo-info: add the --format flag\n3:  3b2f534a69 < -:  ---------- repo-info: add the field references.format\n-:  ---------- > 2:  1cc1184663 repo-info: add the --format flag\n-:  ---------- > 3:  a329825387 repo-info: add plaintext as an output format\n-:  ---------- > 4:  3b6c27b68d repo-info: add the --allow-empty flag\n-:  ---------- > 5:  441b010175 repo-info: add the field references.format\n4:  5ad7e79f83 ! 6:  f39086ea86 repo-info: add field layout.bare\n    @@ builtin/repo-info.c\n     +\n      #include \"builtin.h\"\n     +#include \"environment.h\"\n    - #include \"hash.h\"\n    ++#include \"hash.h\"\n      #include \"json-writer.h\"\n      #include \"parse-options.h\"\n    +-#include \"refs.h\"\n    + #include \"quote.h\"\n    ++#include \"refs.h\"\n    +\n    + enum output_format {\n    + \tFORMAT_JSON,\n     @@ builtin/repo-info.c: enum output_format {\n      };\n\n      enum repo_info_category {\n    --\tCATEGORY_REFERENCES = 1\n    -+\tCATEGORY_REFERENCES = 1,\n    +-\tCATEGORY_REFERENCES = 1 << 0\n    ++\tCATEGORY_REFERENCES = 1 << 0,\n     +\tCATEGORY_LAYOUT = 1 << 1\n      };\n\n      enum repo_info_references_field {\n    - \tFIELD_REFERENCES_FORMAT = 1\n    + \tFIELD_REFERENCES_FORMAT = 1 << 0\n      };\n\n    -+enum repo_info_layout_field { FIELD_LAYOUT_BARE = 1\n    ++enum repo_info_layout_field {\n    ++\tFIELD_LAYOUT_BARE = 1 << 0\n     +};\n     +\n      struct repo_info_field {\n    @@ builtin/repo-info.c: enum output_format {\n      \t} field;\n      };\n\n    -@@ builtin/repo-info.c: struct repo_info {\n    -\n    - const char *default_fields[] = {\n    - \t\"references.format\",\n    -+\t\"layout.bare\"\n    +@@ builtin/repo-info.c: static struct repo_info_field default_fields[] = {\n    + \t{\n    + \t\t.category = CATEGORY_REFERENCES,\n    + \t\t.field.references = FIELD_REFERENCES_FORMAT\n    ++\t},\n    ++\t{\n    ++\t\t.category = CATEGORY_LAYOUT,\n    ++\t\t.field.layout = FIELD_LAYOUT_BARE\n    + \t}\n      };\n\n    - static void repo_info_init(struct repo_info *repo_info,\n     @@ builtin/repo-info.c: static void repo_info_init(struct repo_info *repo_info,\n    - \t\t\tfield->category = CATEGORY_REFERENCES;\n    - \t\t\tfield->field.references = FIELD_REFERENCES_FORMAT;\n    - \t\t}\n    -+\t\telse if (!strcmp(arg, \"layout.bare\")) {\n    -+\t\t\tfield->category = CATEGORY_LAYOUT;\n    -+\t\t\tfield->field.layout = FIELD_LAYOUT_BARE;\n    -+\t\t}\n    - \t\telse {\n    - \t\t\tdie(\"invalid field '%s'\", arg);\n    - \t\t}\n    + \t\t\tif (!strcmp(arg, \"references.format\")) {\n    + \t\t\t\tfield->category = CATEGORY_REFERENCES;\n    + \t\t\t\tfield->field.references = FIELD_REFERENCES_FORMAT;\n    ++\t\t\t} else if (!strcmp(arg, \"layout.bare\")) {\n    ++\t\t\t\tfield->category = CATEGORY_LAYOUT;\n    ++\t\t\t\tfield->field.layout = FIELD_LAYOUT_BARE;\n    + \t\t\t} else {\n    + \t\t\t\tdie(\"invalid field '%s'\", arg);\n    + \t\t\t}\n     @@ builtin/repo-info.c: static void repo_info_print_plaintext(struct repo_info *repo_info) {\n      \t\t\t\tbreak;\n      \t\t\t}\n    @@ builtin/repo-info.c: static void repo_info_print_plaintext(struct repo_info *rep\n     +\t\tcase CATEGORY_LAYOUT:\n     +\t\t\tswitch (field->field.layout) {\n     +\t\t\tcase FIELD_LAYOUT_BARE:\n    -+\t\t\t\tputs(is_bare_repository() ? \"true\" : \"false\");\n    ++\t\t\t\tprint_key_value(\"layout.bare\",\n    ++\t\t\t\t\t\tis_bare_repository() ?\n    ++\t\t\t\t\t\t\t\"true\" : \"false\");\n     +\t\t\t\tbreak;\n     +\t\t\t}\n     +\t\t\tbreak;\n    @@ builtin/repo-info.c: static void repo_info_print_json(struct repo_info *repo_inf\n\n      \tputs(jw.json.buf);\n\n    - ## t/t1518-repo-info.sh ##\n    -@@ t/t1518-repo-info.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n    + ## t/t1900-repo-info.sh ##\n    +@@ t/t1900-repo-info.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n\n      . ./test-lib.sh\n\n    @@ t/t1518-repo-info.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n\n      parse_json () {\n      \ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n    -@@ t/t1518-repo-info.sh: test_repo_info () {\n    - \t\ttest_when_finished 'rm -rf repo' &&\n    - \t\tgit init $init_args repo &&\n    - \t\tcd repo &&\n    --\t\techo '$expected_value' >expect &&\n    -+\t\techo '$expected_value' | sed 's/^false$/0/' | sed 's/^true$/1/' >expect &&\n    - \t\tgit repo-info '$key'| parse_json >output &&\n    - \t\tgrep -F 'row[0].$key' output | cut -d ' ' -f 2 >actual &&\n    - \t\ttest_cmp expect actual\n    -@@ t/t1518-repo-info.sh: test_repo_info 'ref format reftable is retrieved correctly' \\\n    - \t'--ref-format=reftable' \\\n    - \t'references.format' 'reftable'\n    +@@ t/t1900-repo-info.sh: test_repo_info 'ref format files is retrieved correctly' '\n    + test_repo_info 'ref format reftable is retrieved correctly' '\n    + \tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n\n    -+test_repo_info 'bare repository = false is retrieved correctly' \\\n    -+\t'' \\\n    -+\t'layout.bare' 'false'\n    ++test_repo_info 'bare repository = false is retrieved correctly' '\n    ++\tgit init repo' 'layout.bare' 'false'\n     +\n    -+test_repo_info 'bare repository = true is retrieved correctly' \\\n    -+\t'--bare' \\\n    -+\t'layout.bare' 'true'\n    ++test_repo_info 'bare repository = true is retrieved correctly' '\n    ++\tgit init --bare repo' 'layout.bare' 'true'\n     +\n      test_expect_success 'plaintext: output all default fields' \"\n      \tgit repo-info --format=plaintext >actual &&\n5:  0295c19951 < -:  ---------- repo-info: add field layout.shallow\n-:  ---------- > 7:  219b84f032 repo-info: add field layout.shallow\n\nLucas Seiki Oshiro (7):\n  repo-info: declare the repo-info command\n  repo-info: add the --format flag\n  repo-info: add plaintext as an output format\n  repo-info: add the --allow-empty flag\n  repo-info: add the field references.format\n  repo-info: add field layout.bare\n  repo-info: add field layout.shallow\n\n .gitignore           |   1 +\n Makefile             |   1 +\n builtin.h            |   1 +\n builtin/repo-info.c  | 244 +++++++++++++++++++++++++++++++++++++++++++\n git.c                |   1 +\n meson.build          |   1 +\n t/meson.build        |   1 +\n t/t1900-repo-info.sh | 105 +++++++++++++++++++\n 8 files changed, 355 insertions(+)\n create mode 100644 builtin/repo-info.c\n create mode 100755 t/t1900-repo-info.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520428","messageId":"20250619225751.99699-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-19T22:57:45Z","receivedAt":"2025-06-19T22:58:05Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Create a new Git subcommand called repo-info. `git repo-info` will query\nmetadata from the current repository and outputs it as JSON or plaintext.\n\nAlso add entries for this new command in:\n\n- the build files (Makefile and meson.build)\n- builtin.h\n- git.c\n- .gitignore\n\nIn option parsing, use PARSE_OPT_KEEP_UNKNOWN_OPT to allow the users\nspecify after the flags the information that they want to retrieve.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore          |  1 +\n Makefile            |  1 +\n builtin.h           |  1 +\n builtin/repo-info.c | 21 +++++++++++++++++++++\n git.c               |  1 +\n meson.build         |  1 +\n 6 files changed, 26 insertions(+)\n create mode 100644 builtin/repo-info.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..b2f3fb0047 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo-info\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Makefile b/Makefile\nindex 70d1543b6b..50e3a3cbcc 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1308,6 +1308,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo-info.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..cc6bc95962 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo_info(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nnew file mode 100644\nindex 0000000000..a5c43e253f\n--- /dev/null\n+++ b/builtin/repo-info.c\n@@ -0,0 +1,21 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+int cmd_repo_info(int argc,\n+\t\t  const char **argv,\n+\t\t  const char *prefix,\n+\t\t  struct repository *repo UNUSED)\n+{\n+\tconst char *const repo_info_usage[] = {\n+\t\t\"git repo-info\",\n+\t\tNULL\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n+\t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n+\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 07a5fe39fb..27a2b3569b 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo-info\", cmd_repo_info, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 7fea4a34d6..06f2f647ba 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo-info.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520429","messageId":"20250619225751.99699-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v2 2/7] repo-info: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-19T22:57:46Z","receivedAt":"2025-06-19T22:58:08Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the --format flag to the repo-info command, allowing the user to\nchoose between output formats. Use 'json' by default.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 54 +++++++++++++++++++++++++++++++++++++++++++-\n t/meson.build        |  1 +\n t/t1900-repo-info.sh | 22 ++++++++++++++++++\n 3 files changed, 76 insertions(+), 1 deletion(-)\n create mode 100755 t/t1900-repo-info.sh\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex a5c43e253f..cbe1475e30 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -1,21 +1,73 @@\n #include \"builtin.h\"\n+#include \"json-writer.h\"\n #include \"parse-options.h\"\n \n+enum output_format {\n+\tFORMAT_JSON\n+};\n+\n+struct repo_info {\n+\tstruct repository *repo;\n+\tenum output_format format;\n+};\n+\n+static void repo_info_init(struct repo_info *repo_info,\n+\t\t\t   struct repository *repo,\n+\t\t\t   char *format)\n+{\n+\trepo_info->repo = repo;\n+\n+\tif (format == NULL || !strcmp(format, \"json\"))\n+\t\trepo_info->format = FORMAT_JSON;\n+\telse\n+\t\tdie(\"invalid format %s\", format);\n+}\n+\n+static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n+{\n+\tstruct json_writer jw;\n+\n+\tjw_init(&jw);\n+\n+\tjw_object_begin(&jw, 1);\n+\tjw_end(&jw);\n+\n+\tputs(jw.json.buf);\n+\tjw_release(&jw);\n+}\n+\n+static void repo_info_print(struct repo_info *repo_info)\n+{\n+\tenum output_format format = repo_info->format;\n+\n+\tswitch (format) {\n+\tcase FORMAT_JSON:\n+\t\trepo_info_print_json(repo_info);\n+\t\tbreak;\n+\t}\n+}\n+\n int cmd_repo_info(int argc,\n \t\t  const char **argv,\n \t\t  const char *prefix,\n-\t\t  struct repository *repo UNUSED)\n+\t\t  struct repository *repo)\n {\n \tconst char *const repo_info_usage[] = {\n \t\t\"git repo-info\",\n \t\tNULL\n \t};\n+\tstruct repo_info repo_info;\n+\tchar *format = NULL;\n \tstruct option options[] = {\n+\t\tOPT_STRING(0, \"format\", &format, N_(\"format\"),\n+\t\t\t   N_(\"output format\")),\n \t\tOPT_END()\n \t};\n \n \targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n+\trepo_info_init(&repo_info, repo, format);\n+\trepo_info_print(&repo_info);\n \n \treturn 0;\n }\ndiff --git a/t/meson.build b/t/meson.build\nindex 50e89e764a..d9ecaba3b7 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -246,6 +246,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo-info.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nnew file mode 100755\nindex 0000000000..f634e1a285\n--- /dev/null\n+++ b/t/t1900-repo-info.sh\n@@ -0,0 +1,22 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n+\n+. ./test-lib.sh\n+\n+parse_json () {\n+\ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n+}\n+\n+test_lazy_prereq PERLJSON '\n+\tperl -MJSON -e \"exit 0\"\n+'\n+\n+test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n+\tgit repo-info --format=json >output &&\n+\ttest_line_count = 2 output\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520430","messageId":"20250619225751.99699-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v2 3/7] repo-info: add plaintext as an output format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-19T22:57:47Z","receivedAt":"2025-06-19T22:58:10Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add 'plaintext' as an output format of repo-info. This output format is\ncomposed zero or more key=value pairs, one per line.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 12 +++++++++++-\n t/t1900-repo-info.sh |  4 ++++\n 2 files changed, 15 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex cbe1475e30..cd7c110f47 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -3,7 +3,8 @@\n #include \"parse-options.h\"\n \n enum output_format {\n-\tFORMAT_JSON\n+\tFORMAT_JSON,\n+\tFORMAT_PLAINTEXT\n };\n \n struct repo_info {\n@@ -19,10 +20,16 @@ static void repo_info_init(struct repo_info *repo_info,\n \n \tif (format == NULL || !strcmp(format, \"json\"))\n \t\trepo_info->format = FORMAT_JSON;\n+\telse if (!strcmp(format, \"plaintext\"))\n+\t\trepo_info->format = FORMAT_PLAINTEXT;\n \telse\n \t\tdie(\"invalid format %s\", format);\n }\n \n+static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED)\n+{\n+}\n+\n static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n {\n \tstruct json_writer jw;\n@@ -44,6 +51,9 @@ static void repo_info_print(struct repo_info *repo_info)\n \tcase FORMAT_JSON:\n \t\trepo_info_print_json(repo_info);\n \t\tbreak;\n+\tcase FORMAT_PLAINTEXT:\n+\t\trepo_info_print_plaintext(repo_info);\n+\t\tbreak;\n \t}\n }\n \ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex f634e1a285..998c835795 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -18,5 +18,9 @@ test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n \tgit repo-info --format=json >output &&\n \ttest_line_count = 2 output\n '\n+test_expect_success 'plaintext: returns empty output with allow-empty' '\n+\tgit repo-info --format=plaintext >output &&\n+\ttest_line_count = 0 output\n+'\n \n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520431","messageId":"20250619225751.99699-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v2 4/7] repo-info: add the --allow-empty flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-19T22:57:48Z","receivedAt":"2025-06-19T22:58:13Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add a flag --allow-empty, which will force the output data to be empty\nwhen no field is requested.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 3 +++\n t/t1900-repo-info.sh | 5 +++--\n 2 files changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex cd7c110f47..6499be0eae 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -68,9 +68,12 @@ int cmd_repo_info(int argc,\n \t};\n \tstruct repo_info repo_info;\n \tchar *format = NULL;\n+\tint allow_empty = 0;\n \tstruct option options[] = {\n \t\tOPT_STRING(0, \"format\", &format, N_(\"format\"),\n \t\t\t   N_(\"output format\")),\n+\t\tOPT_BOOL(0, \"allow-empty\", &allow_empty,\n+\t\t\t \"when set, it will use an empty set of fields if no field is requested\"),\n \t\tOPT_END()\n \t};\n \ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 998c835795..db4a6aad17 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -15,11 +15,12 @@ test_lazy_prereq PERLJSON '\n '\n \n test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n-\tgit repo-info --format=json >output &&\n+\tgit repo-info --allow-empty --format=json >output &&\n \ttest_line_count = 2 output\n '\n+\n test_expect_success 'plaintext: returns empty output with allow-empty' '\n-\tgit repo-info --format=plaintext >output &&\n+\tgit repo-info --allow-empty --format=plaintext >output &&\n \ttest_line_count = 0 output\n '\n \n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520432","messageId":"20250619225751.99699-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v2 5/7] repo-info: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-19T22:57:49Z","receivedAt":"2025-06-19T22:58:16Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the field references.format to the repo-info command. The data\nretrieved in this field is the same that currently is obtained by\nrunning `git rev-parse --show-ref-format`.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 108 +++++++++++++++++++++++++++++++++++++++++--\n t/t1900-repo-info.sh |  58 +++++++++++++++++++++++\n 2 files changed, 162 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex 6499be0eae..6ce3e6134f 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -1,21 +1,56 @@\n #include \"builtin.h\"\n #include \"json-writer.h\"\n #include \"parse-options.h\"\n+#include \"quote.h\"\n+#include \"refs.h\"\n \n enum output_format {\n \tFORMAT_JSON,\n \tFORMAT_PLAINTEXT\n };\n \n+enum repo_info_category {\n+\tCATEGORY_REFERENCES = 1 << 0\n+};\n+\n+enum repo_info_references_field {\n+\tFIELD_REFERENCES_FORMAT = 1 << 0\n+};\n+\n+struct repo_info_field {\n+\tenum repo_info_category category;\n+\tunion {\n+\t\tenum repo_info_references_field references;\n+\t} field;\n+};\n+\n struct repo_info {\n \tstruct repository *repo;\n \tenum output_format format;\n+\tint n_fields;\n+\tstruct repo_info_field *fields;\n };\n \n+static struct repo_info_field default_fields[] = {\n+\t{\n+\t\t.category = CATEGORY_REFERENCES,\n+\t\t.field.references = FIELD_REFERENCES_FORMAT\n+\t}\n+};\n+\n+static void print_key_value(const char *key, const char *value) {\n+\tprintf(\"%s=\", key);\n+\tquote_c_style(value, NULL, stdout, 0);\n+\tputchar('\\n');\n+}\n+\n static void repo_info_init(struct repo_info *repo_info,\n \t\t\t   struct repository *repo,\n-\t\t\t   char *format)\n+\t\t\t   char *format,\n+\t\t\t   int allow_empty,\n+\t\t\t   int argc, const char **argv)\n {\n+\tint i;\n \trepo_info->repo = repo;\n \n \tif (format == NULL || !strcmp(format, \"json\"))\n@@ -24,19 +59,83 @@ static void repo_info_init(struct repo_info *repo_info,\n \t\trepo_info->format = FORMAT_PLAINTEXT;\n \telse\n \t\tdie(\"invalid format %s\", format);\n+\n+\tif (argc == 0 && !allow_empty) {\n+\t\trepo_info->n_fields = ARRAY_SIZE(default_fields);\n+\t\trepo_info->fields = default_fields;\n+\t} else {\n+\t\trepo_info->n_fields = argc;\n+\t\tALLOC_ARRAY(repo_info->fields, argc);\n+\n+\t\tfor (i = 0; i < argc; i++) {\n+\t\t\tconst char *arg = argv[i];\n+\t\t\tstruct repo_info_field *field = repo_info->fields + i;\n+\n+\t\t\tif (!strcmp(arg, \"references.format\")) {\n+\t\t\t\tfield->category = CATEGORY_REFERENCES;\n+\t\t\t\tfield->field.references = FIELD_REFERENCES_FORMAT;\n+\t\t\t} else {\n+\t\t\t\tdie(\"invalid field '%s'\", arg);\n+\t\t\t}\n+\t\t}\n+\t}\n }\n \n-static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED)\n+static void repo_info_release(struct repo_info *repo_info)\n {\n+\tif (repo_info->fields != default_fields) free(repo_info->fields);\n }\n \n-static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n+static void repo_info_print_plaintext(struct repo_info *repo_info) {\n+\tstruct repository *repo = repo_info->repo;\n+\tint i;\n+\tfor (i = 0; i < repo_info->n_fields; i++) {\n+\t\tstruct repo_info_field *field = &repo_info->fields[i];\n+\t\tswitch (field->category) {\n+\t\tcase CATEGORY_REFERENCES:\n+\t\t\tswitch (field->field.references) {\n+\t\t\tcase FIELD_REFERENCES_FORMAT:\n+\t\t\t\tprint_key_value(\"references.format\",\n+\t\t\t\t\t\tref_storage_format_to_name(\n+\t\t\t\t\t\t\trepo->ref_storage_format));\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n+static void repo_info_print_json(struct repo_info *repo_info)\n {\n \tstruct json_writer jw;\n+\tint i;\n+\tunsigned int categories = 0;\n+\tunsigned int references_fields = 0;\n+\tstruct repository *repo = repo_info->repo;\n+\n+\tfor (i = 0; i < repo_info->n_fields; i++) {\n+\t\tstruct repo_info_field *field = repo_info->fields + i;\n+\t\tcategories |= field->category;\n+\t\tswitch (field->category) {\n+\t\tcase CATEGORY_REFERENCES:\n+\t\t\treferences_fields |= field->field.references;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n \n \tjw_init(&jw);\n \n \tjw_object_begin(&jw, 1);\n+\n+\tif (categories & CATEGORY_REFERENCES) {\n+\t\tjw_object_inline_begin_object(&jw, \"references\");\n+\t\tif (references_fields & FIELD_REFERENCES_FORMAT) {\n+\t\t\tconst char *format_name = ref_storage_format_to_name(\n+\t\t\t\trepo->ref_storage_format);\n+\t\t\tjw_object_string(&jw, \"format\", format_name);\n+\t\t}\n+\t\tjw_end(&jw);\n+\t}\n \tjw_end(&jw);\n \n \tputs(jw.json.buf);\n@@ -79,8 +178,9 @@ int cmd_repo_info(int argc,\n \n \targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n \t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n-\trepo_info_init(&repo_info, repo, format);\n+\trepo_info_init(&repo_info, repo, format, allow_empty, argc, argv);\n \trepo_info_print(&repo_info);\n+\trepo_info_release(&repo_info);\n \n \treturn 0;\n }\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex db4a6aad17..d6e6f6ed1d 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -6,6 +6,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n+DEFAULT_NUMBER_OF_FIELDS=1\n+\n parse_json () {\n \ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n }\n@@ -14,6 +16,45 @@ test_lazy_prereq PERLJSON '\n \tperl -MJSON -e \"exit 0\"\n '\n \n+# Test if a field is correctly returned in both plaintext and json formats.\n+#\n+# Usage: test_repo_info <label> <init command> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init command: a command that creates a repository called 'repo', configured\n+#      accordingly to what is being tested\n+#   key: the key of the field that is being tested\n+#   expected value: the value that the field should contain\n+test_repo_info () {\n+        label=$1\n+        init_command=$2\n+        key=$3\n+        expected_value=$4\n+\n+        test_expect_success PERLJSON \"json: $label\" \"\n+                test_when_finished 'rm -rf repo' &&\n+                '$SHELL_PATH' -c '$init_command' &&\n+                cd repo &&\n+                echo '$expected_value' >expect &&\n+                git repo-info '$key' >output &&\n+                cat output | parse_json >parsed &&\n+                grep -F 'row[0].$key' parsed | cut -d ' ' -f 2 >value &&\n+                cat value | sed 's/^0$/false/' | sed 's/^1$/true/' >actual &&\n+                test_cmp expect actual\n+        \"\n+\n+        test_expect_success \"plaintext: $label\" \"\n+                test_when_finished 'rm -rf repo' &&\n+                '$SHELL_PATH' -c '$init_command' &&\n+                cd repo &&\n+                echo '$expected_value' >expect &&\n+                git repo-info --format=plaintext '$key' >output &&\n+                cat output | cut -d '=' -f 2 >actual &&\n+                test_cmp expect actual\n+        \"\n+}\n+\n test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n \tgit repo-info --allow-empty --format=json >output &&\n \ttest_line_count = 2 output\n@@ -24,4 +65,21 @@ test_expect_success 'plaintext: returns empty output with allow-empty' '\n \ttest_line_count = 0 output\n '\n \n+test_repo_info 'ref format files is retrieved correctly' '\n+\tgit init --ref-format=files repo' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' '\n+\tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n+\n+test_expect_success 'plaintext: output all default fields' \"\n+\tgit repo-info --format=plaintext >actual &&\n+\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n+\"\n+\n+test_expect_success PERLJSON 'json: output all default fields' \"\n+\tgit repo-info --format=json > output &&\n+\tcat output | parse_json | grep '.*\\..*\\..*' >actual &&\n+\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n+\"\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520433","messageId":"20250619225751.99699-7-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v2 6/7] repo-info: add field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-19T22:57:50Z","receivedAt":"2025-06-19T22:58:19Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the field layout.bare to the repo-info command. The data\nretrieved in this field is the same that currently is obtained by\nrunning `git rev-parse --is-bare-repository`.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 41 ++++++++++++++++++++++++++++++++++++++++-\n t/t1900-repo-info.sh |  8 +++++++-\n 2 files changed, 47 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex 6ce3e6134f..1650d3595c 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -1,4 +1,8 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n+#include \"environment.h\"\n+#include \"hash.h\"\n #include \"json-writer.h\"\n #include \"parse-options.h\"\n #include \"quote.h\"\n@@ -10,17 +14,23 @@ enum output_format {\n };\n \n enum repo_info_category {\n-\tCATEGORY_REFERENCES = 1 << 0\n+\tCATEGORY_REFERENCES = 1 << 0,\n+\tCATEGORY_LAYOUT = 1 << 1\n };\n \n enum repo_info_references_field {\n \tFIELD_REFERENCES_FORMAT = 1 << 0\n };\n \n+enum repo_info_layout_field {\n+\tFIELD_LAYOUT_BARE = 1 << 0\n+};\n+\n struct repo_info_field {\n \tenum repo_info_category category;\n \tunion {\n \t\tenum repo_info_references_field references;\n+\t\tenum repo_info_layout_field layout;\n \t} field;\n };\n \n@@ -35,6 +45,10 @@ static struct repo_info_field default_fields[] = {\n \t{\n \t\t.category = CATEGORY_REFERENCES,\n \t\t.field.references = FIELD_REFERENCES_FORMAT\n+\t},\n+\t{\n+\t\t.category = CATEGORY_LAYOUT,\n+\t\t.field.layout = FIELD_LAYOUT_BARE\n \t}\n };\n \n@@ -74,6 +88,9 @@ static void repo_info_init(struct repo_info *repo_info,\n \t\t\tif (!strcmp(arg, \"references.format\")) {\n \t\t\t\tfield->category = CATEGORY_REFERENCES;\n \t\t\t\tfield->field.references = FIELD_REFERENCES_FORMAT;\n+\t\t\t} else if (!strcmp(arg, \"layout.bare\")) {\n+\t\t\t\tfield->category = CATEGORY_LAYOUT;\n+\t\t\t\tfield->field.layout = FIELD_LAYOUT_BARE;\n \t\t\t} else {\n \t\t\t\tdie(\"invalid field '%s'\", arg);\n \t\t\t}\n@@ -101,6 +118,15 @@ static void repo_info_print_plaintext(struct repo_info *repo_info) {\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\tbreak;\n+\t\tcase CATEGORY_LAYOUT:\n+\t\t\tswitch (field->field.layout) {\n+\t\t\tcase FIELD_LAYOUT_BARE:\n+\t\t\t\tprint_key_value(\"layout.bare\",\n+\t\t\t\t\t\tis_bare_repository() ?\n+\t\t\t\t\t\t\t\"true\" : \"false\");\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tbreak;\n \t\t}\n \t}\n }\n@@ -111,6 +137,7 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \tint i;\n \tunsigned int categories = 0;\n \tunsigned int references_fields = 0;\n+\tunsigned int layout_fields = 0;\n \tstruct repository *repo = repo_info->repo;\n \n \tfor (i = 0; i < repo_info->n_fields; i++) {\n@@ -120,6 +147,9 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \t\tcase CATEGORY_REFERENCES:\n \t\t\treferences_fields |= field->field.references;\n \t\t\tbreak;\n+\t\tcase CATEGORY_LAYOUT:\n+\t\t\tlayout_fields |= field->field.layout;\n+\t\t\tbreak;\n \t\t}\n \t}\n \n@@ -136,6 +166,15 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \t\t}\n \t\tjw_end(&jw);\n \t}\n+\n+\tif (categories & CATEGORY_LAYOUT) {\n+\t\tjw_object_inline_begin_object(&jw, \"layout\");\n+\t\tif (layout_fields & FIELD_LAYOUT_BARE) {\n+\t\t\tjw_object_bool(&jw, \"bare\",\n+\t\t\t\t       is_bare_repository());\n+\t\t}\n+\t\tjw_end(&jw);\n+\t}\n \tjw_end(&jw);\n \n \tputs(jw.json.buf);\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex d6e6f6ed1d..0d1096b40b 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -6,7 +6,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n-DEFAULT_NUMBER_OF_FIELDS=1\n+DEFAULT_NUMBER_OF_FIELDS=2\n \n parse_json () {\n \ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n@@ -71,6 +71,12 @@ test_repo_info 'ref format files is retrieved correctly' '\n test_repo_info 'ref format reftable is retrieved correctly' '\n \tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' '\n+\tgit init --bare repo' 'layout.bare' 'true'\n+\n test_expect_success 'plaintext: output all default fields' \"\n \tgit repo-info --format=plaintext >actual &&\n \ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520434","messageId":"20250619225751.99699-8-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v2 7/7] repo-info: add field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-19T22:57:51Z","receivedAt":"2025-06-19T22:58:22Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the field layout.shallow to the repo-info command. The data\nretrieved in this field is the same that currently is obtained by\nrunning `git rev-parse --is-shallow-repository`.\n\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n builtin/repo-info.c  | 21 ++++++++++++++++++++-\n t/t1900-repo-info.sh | 16 +++++++++++++++-\n 2 files changed, 35 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex 1650d3595c..4c265c05fa 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"quote.h\"\n #include \"refs.h\"\n+#include \"shallow.h\"\n \n enum output_format {\n \tFORMAT_JSON,\n@@ -23,7 +24,8 @@ enum repo_info_references_field {\n };\n \n enum repo_info_layout_field {\n-\tFIELD_LAYOUT_BARE = 1 << 0\n+\tFIELD_LAYOUT_BARE = 1 << 0,\n+\tFIELD_LAYOUT_SHALLOW = 1 << 1\n };\n \n struct repo_info_field {\n@@ -49,6 +51,10 @@ static struct repo_info_field default_fields[] = {\n \t{\n \t\t.category = CATEGORY_LAYOUT,\n \t\t.field.layout = FIELD_LAYOUT_BARE\n+\t},\n+\t{\n+\t\t.category = CATEGORY_LAYOUT,\n+\t\t.field.layout = FIELD_LAYOUT_SHALLOW\n \t}\n };\n \n@@ -91,6 +97,9 @@ static void repo_info_init(struct repo_info *repo_info,\n \t\t\t} else if (!strcmp(arg, \"layout.bare\")) {\n \t\t\t\tfield->category = CATEGORY_LAYOUT;\n \t\t\t\tfield->field.layout = FIELD_LAYOUT_BARE;\n+\t\t\t} else if (!strcmp(arg, \"layout.shallow\")) {\n+\t\t\t\tfield->category = CATEGORY_LAYOUT;\n+\t\t\t\tfield->field.layout = FIELD_LAYOUT_SHALLOW;\n \t\t\t} else {\n \t\t\t\tdie(\"invalid field '%s'\", arg);\n \t\t\t}\n@@ -125,6 +134,11 @@ static void repo_info_print_plaintext(struct repo_info *repo_info) {\n \t\t\t\t\t\tis_bare_repository() ?\n \t\t\t\t\t\t\t\"true\" : \"false\");\n \t\t\t\tbreak;\n+\t\t\tcase FIELD_LAYOUT_SHALLOW:\n+\t\t\t\tprint_key_value(\"layout.shallow\",\n+\t\t\t\t\t\tis_repository_shallow(repo) ?\n+\t\t\t\t\t\t\t\"true\" : \"false\");\n+\t\t\t\tbreak;\n \t\t\t}\n \t\t\tbreak;\n \t\t}\n@@ -173,6 +187,11 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \t\t\tjw_object_bool(&jw, \"bare\",\n \t\t\t\t       is_bare_repository());\n \t\t}\n+\n+\t\tif (layout_fields & FIELD_LAYOUT_SHALLOW) {\n+\t\t\tjw_object_bool(&jw, \"shallow\",\n+\t\t\t\t       is_repository_shallow(repo));\n+\t\t}\n \t\tjw_end(&jw);\n \t}\n \tjw_end(&jw);\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 0d1096b40b..5cd2f8d187 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -6,7 +6,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n-DEFAULT_NUMBER_OF_FIELDS=2\n+DEFAULT_NUMBER_OF_FIELDS=3\n \n parse_json () {\n \ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n@@ -77,6 +77,20 @@ test_repo_info 'bare repository = false is retrieved correctly' '\n test_repo_info 'bare repository = true is retrieved correctly' '\n \tgit init --bare repo' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.shallow' 'false'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' '\n+\tgit init remote &&\n+\tcd remote &&\n+\techo x >x &&\n+\tgit add x &&\n+\tgit commit -m x &&\n+\tcd .. &&\n+\tgit clone --depth 1 \"file://$PWD/remote\" repo &&\n+\trm -rf remote\n+\t' 'layout.shallow' 'true'\n+\n test_expect_success 'plaintext: output all default fields' \"\n \tgit repo-info --format=plaintext >actual &&\n \ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"520484","messageId":"CAOLa=ZSgFYXEwdTyAfC2TSgVqpCSq6D1NDBwPU9cY-unX0Jx6Q@mail.gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-20T07:36:52Z","receivedAt":"2025-06-20T07:36:54Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Create a new Git subcommand called repo-info. `git repo-info` will query\n> metadata from the current repository and outputs it as JSON or plaintext.\n>\n> Also add entries for this new command in:\n>\n> - the build files (Makefile and meson.build)\n> - builtin.h\n> - git.c\n> - .gitignore\n>\n> In option parsing, use PARSE_OPT_KEEP_UNKNOWN_OPT to allow the users\n> specify after the flags the information that they want to retrieve.\n>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  .gitignore          |  1 +\n>  Makefile            |  1 +\n>  builtin.h           |  1 +\n>  builtin/repo-info.c | 21 +++++++++++++++++++++\n>  git.c               |  1 +\n>  meson.build         |  1 +\n>  6 files changed, 26 insertions(+)\n>  create mode 100644 builtin/repo-info.c\n>\n\nSomething I mentioned in the previous review, but hasn't been addressed\nis the addition of documentation for the new command. With each patch\nmodifying the command, it would be also nice to add the respective\ndocumentation.\n"},{"id":"520485","messageId":"CAOLa=ZSsY-B2kxfuiuuE7drO-HueTwTvNCBQANkZP-4KxZbJ=g@mail.gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-20T07:56:17Z","receivedAt":"2025-06-20T07:56:19Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Create a new Git subcommand called repo-info. `git repo-info` will query\n> metadata from the current repository and outputs it as JSON or plaintext.\n>\n> Also add entries for this new command in:\n>\n> - the build files (Makefile and meson.build)\n> - builtin.h\n> - git.c\n> - .gitignore\n>\n> In option parsing, use PARSE_OPT_KEEP_UNKNOWN_OPT to allow the users\n> specify after the flags the information that they want to retrieve.\n>\n\nSomething I would really recommend in all the commits is to be a lot\nmore descriptive about _why_ are we even making these changes.\n\nIn this case, it would be great to mention about 'git-rev-parse(1)' and\nthe motivation of adding this new command. That would provide some much\nneeded context about why we are even adding this new command. Also worth\nmentioning is why we plan to output in JSON or plaintext.\n\n- Karthik\n"},{"id":"520486","messageId":"CAOLa=ZT=rwejgefvQcxwLBUS3ExoO_G_r0ruBoQZWKDR-LfzvQ@mail.gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 2/7] repo-info: add the --format flag","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-20T08:06:41Z","receivedAt":"2025-06-20T08:06:43Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add the --format flag to the repo-info command, allowing the user to\n> choose between output formats. Use 'json' by default.\n\nSimilarly here, it would be nice to mention why we add 'json' formatting\nand why it is the default.\n\n[snip]\n\n> diff --git a/t/meson.build b/t/meson.build\n> index 50e89e764a..d9ecaba3b7 100644\n> --- a/t/meson.build\n> +++ b/t/meson.build\n> @@ -246,6 +246,7 @@ integration_tests = [\n>    't1700-split-index.sh',\n>    't1701-racy-split-index.sh',\n>    't1800-hook.sh',\n> +  't1900-repo-info.sh',\n>    't2000-conflict-when-checking-files-out.sh',\n>    't2002-checkout-cache-u.sh',\n>    't2003-checkout-cache-mkdir.sh',\n> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\n> new file mode 100755\n> index 0000000000..f634e1a285\n> --- /dev/null\n> +++ b/t/t1900-repo-info.sh\n> @@ -0,0 +1,22 @@\n> +#!/bin/sh\n> +\n> +test_description='test git repo-info'\n> +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> +\n> +. ./test-lib.sh\n> +\n> +parse_json () {\n> +\ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n> +}\n> +\n> +test_lazy_prereq PERLJSON '\n> +\tperl -MJSON -e \"exit 0\"\n> +'\n> +\n> +test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n\nHuh. Where is allow-empty being used? and why in this patch?\n\n> +\tgit repo-info --format=json >output &&\n> +\ttest_line_count = 2 output\n> +'\n>\n"},{"id":"520487","messageId":"CAOLa=ZQXeQX-v2JmKTpaJDLwWWKQSXoMUGbpmD6xx8KpbY91yw@mail.gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-5-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 4/7] repo-info: add the --allow-empty flag","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-20T09:54:21Z","receivedAt":"2025-06-20T09:54:23Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add a flag --allow-empty, which will force the output data to be empty\n> when no field is requested.\n>\n\nWhile you did reply to my previous question about this flag. This patch\ndoesn't sell the need for it in anyway.\n\nSo we need:\n1. More description in the commit messages about why we need this flag\nand how it is useful.\n2. Documentation around the same.\n\nI still think this is early for this, and we should add this flag later\nif the need arises.\n\n[snip]\n\n> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\n> index 998c835795..db4a6aad17 100755\n> --- a/t/t1900-repo-info.sh\n> +++ b/t/t1900-repo-info.sh\n> @@ -15,11 +15,12 @@ test_lazy_prereq PERLJSON '\n>  '\n>\n>  test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n> -\tgit repo-info --format=json >output &&\n> +\tgit repo-info --allow-empty --format=json >output &&\n>  \ttest_line_count = 2 output\n>  '\n> +\n>  test_expect_success 'plaintext: returns empty output with allow-empty' '\n> -\tgit repo-info --format=plaintext >output &&\n> +\tgit repo-info --allow-empty --format=plaintext >output &&\n>  \ttest_line_count = 0 output\n>  '\n\nThe earlier patches reference allow-empty without really talking about\nit. Which feels odd and misplaced.\n"},{"id":"520519","messageId":"xmqqecve14o6.fsf@gitster.g","threadId":"63618","inReplyTo":"20250619225751.99699-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 2/7] repo-info: add the --format flag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T21:31:05Z","receivedAt":"2025-06-20T21:31:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add the --format flag to the repo-info command, allowing the user to\n> choose between output formats. Use 'json' by default.\n>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  builtin/repo-info.c  | 54 +++++++++++++++++++++++++++++++++++++++++++-\n>  t/meson.build        |  1 +\n>  t/t1900-repo-info.sh | 22 ++++++++++++++++++\n>  3 files changed, 76 insertions(+), 1 deletion(-)\n>  create mode 100755 t/t1900-repo-info.sh\n>\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index a5c43e253f..cbe1475e30 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -1,21 +1,73 @@\n>  #include \"builtin.h\"\n> +#include \"json-writer.h\"\n>  #include \"parse-options.h\"\n>  \n> +enum output_format {\n> +\tFORMAT_JSON\n> +};\n\nIf you give a trailing comma after FORMAT_JSON here, then a future\nstep that adds other values to the enum would not have to touch this\nline, e.g. instead of\n\n     enum output_format {\n    -    FORMAT_JSON\n    +    FORMAT_JSON,\n    +    FORMAT_FOO\n     };\n\nyou can do this\n\n     enum output_format {\n         FORMAT_JSON,\n    +    FORMAT_FOO,\n     };\n\nwhen you add a new value \"FOO\" to enum.\n\ncf. Documentation/CodingGuidelines\n\n   . since early 2012 with e1327023ea, we have been using an enum\n     definition whose last element is followed by a comma.  This, like\n     an array initializer that ends with a trailing comma, can be used\n     to reduce the patch noise when adding a new identifier at the end.\n\n> +struct repo_info {\n> +\tstruct repository *repo;\n> +\tenum output_format format;\n> +};\n> +\n> +static void repo_info_init(struct repo_info *repo_info,\n> +\t\t\t   struct repository *repo,\n> +\t\t\t   char *format)\n> +{\n> +\trepo_info->repo = repo;\n> +\n> +\tif (format == NULL || !strcmp(format, \"json\"))\n> +\t\trepo_info->format = FORMAT_JSON;\n> +\telse\n> +\t\tdie(\"invalid format %s\", format);\n> +}\n> +\n> +static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n> +{\n> +\tstruct json_writer jw;\n> +\n> +\tjw_init(&jw);\n> +\n> +\tjw_object_begin(&jw, 1);\n> +\tjw_end(&jw);\n> +\n> +\tputs(jw.json.buf);\n> +\tjw_release(&jw);\n> +}\n> +\n> +static void repo_info_print(struct repo_info *repo_info)\n> +{\n> +\tenum output_format format = repo_info->format;\n> +\n> +\tswitch (format) {\n> +\tcase FORMAT_JSON:\n> +\t\trepo_info_print_json(repo_info);\n> +\t\tbreak;\n> +\t}\n> +}\n> +\n>  int cmd_repo_info(int argc,\n>  \t\t  const char **argv,\n>  \t\t  const char *prefix,\n> -\t\t  struct repository *repo UNUSED)\n> +\t\t  struct repository *repo)\n>  {\n>  \tconst char *const repo_info_usage[] = {\n>  \t\t\"git repo-info\",\n>  \t\tNULL\n>  \t};\n> +\tstruct repo_info repo_info;\n> +\tchar *format = NULL;\n>  \tstruct option options[] = {\n> +\t\tOPT_STRING(0, \"format\", &format, N_(\"format\"),\n> +\t\t\t   N_(\"output format\")),\n>  \t\tOPT_END()\n>  \t};\n>  \n>  \targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n>  \t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n> +\trepo_info_init(&repo_info, repo, format);\n> +\trepo_info_print(&repo_info);\n>  \n>  \treturn 0;\n>  }\n> diff --git a/t/meson.build b/t/meson.build\n> index 50e89e764a..d9ecaba3b7 100644\n> --- a/t/meson.build\n> +++ b/t/meson.build\n> @@ -246,6 +246,7 @@ integration_tests = [\n>    't1700-split-index.sh',\n>    't1701-racy-split-index.sh',\n>    't1800-hook.sh',\n> +  't1900-repo-info.sh',\n>    't2000-conflict-when-checking-files-out.sh',\n>    't2002-checkout-cache-u.sh',\n>    't2003-checkout-cache-mkdir.sh',\n> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\n> new file mode 100755\n> index 0000000000..f634e1a285\n> --- /dev/null\n> +++ b/t/t1900-repo-info.sh\n> @@ -0,0 +1,22 @@\n> +#!/bin/sh\n> +\n> +test_description='test git repo-info'\n> +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> +\n> +. ./test-lib.sh\n> +\n> +parse_json () {\n> +\ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n> +}\n> +\n> +test_lazy_prereq PERLJSON '\n> +\tperl -MJSON -e \"exit 0\"\n> +'\n> +\n> +test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n\nAllow-empty?\n\nInstead of \"if no command line args, list everything, or show only\nthe ones specified on the command line\", wouldn't it be far easier\nto explain if you made it \"we show only the ones specified on the\ncommand line, but giving '--all' on the command line behaves as if\nyou listed all the ones known to us on the command line\"?\n\n> +\tgit repo-info --format=json >output &&\n> +\ttest_line_count = 2 output\n> +'\n> +\n> +test_done\n"},{"id":"520520","messageId":"xmqq5xgq14dw.fsf@gitster.g","threadId":"63618","inReplyTo":"20250619225751.99699-4-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 3/7] repo-info: add plaintext as an output format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T21:37:15Z","receivedAt":"2025-06-20T21:37:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add 'plaintext' as an output format of repo-info. This output format is\n> composed zero or more key=value pairs, one per line.\n>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  builtin/repo-info.c  | 12 +++++++++++-\n>  t/t1900-repo-info.sh |  4 ++++\n>  2 files changed, 15 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index cbe1475e30..cd7c110f47 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -3,7 +3,8 @@\n>  #include \"parse-options.h\"\n>  \n>  enum output_format {\n> -\tFORMAT_JSON\n> +\tFORMAT_JSON,\n> +\tFORMAT_PLAINTEXT\n>  };\n\nGive a trailing comma after \"_PLAINTEXT\".\n\n>  struct repo_info {\n> @@ -19,10 +20,16 @@ static void repo_info_init(struct repo_info *repo_info,\n>  \n>  \tif (format == NULL || !strcmp(format, \"json\"))\n>  \t\trepo_info->format = FORMAT_JSON;\n\nIt is somewhat strange for this helper function deciding/hardcoding\nwhat the default format is.  Would it make it easier to understand\nif you lost \"if given NULL, use JSON\" from here, and instead\ninitialize the local variable format to \"json\" in cmd_repo_info()?\n\n> +\telse if (!strcmp(format, \"plaintext\"))\n> +\t\trepo_info->format = FORMAT_PLAINTEXT;\n>  \telse\n>  \t\tdie(\"invalid format %s\", format);\n>  }\n>  \n> +static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED)\n> +{\n> +}\n> +\n>  static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n>  {\n>  \tstruct json_writer jw;\n> @@ -44,6 +51,9 @@ static void repo_info_print(struct repo_info *repo_info)\n>  \tcase FORMAT_JSON:\n>  \t\trepo_info_print_json(repo_info);\n>  \t\tbreak;\n> +\tcase FORMAT_PLAINTEXT:\n> +\t\trepo_info_print_plaintext(repo_info);\n> +\t\tbreak;\n>  \t}\n>  }\n>  \n> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\n> index f634e1a285..998c835795 100755\n> --- a/t/t1900-repo-info.sh\n> +++ b/t/t1900-repo-info.sh\n> @@ -18,5 +18,9 @@ test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n>  \tgit repo-info --format=json >output &&\n>  \ttest_line_count = 2 output\n>  '\n> +test_expect_success 'plaintext: returns empty output with allow-empty' '\n\nallow-empty?\n\n> +\tgit repo-info --format=plaintext >output &&\n> +\ttest_line_count = 0 output\n> +'\n\nIn any case, I do not know if it makes sense to add this test before\nyou have a single line of repo_info_print_plaintext() implemented.\n"},{"id":"520521","messageId":"xmqq1pre14ae.fsf@gitster.g","threadId":"63618","inReplyTo":"20250619225751.99699-5-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 4/7] repo-info: add the --allow-empty flag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T21:39:21Z","receivedAt":"2025-06-20T21:39:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add a flag --allow-empty, which will force the output data to be empty\n> when no field is requested.\n\nI still think we should flip the polarity of the option and give \"--all\"\nto iterate over all variables, and by default show nothing.\n\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  builtin/repo-info.c  | 3 +++\n>  t/t1900-repo-info.sh | 5 +++--\n>  2 files changed, 6 insertions(+), 2 deletions(-)\n>\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index cd7c110f47..6499be0eae 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -68,9 +68,12 @@ int cmd_repo_info(int argc,\n>  \t};\n>  \tstruct repo_info repo_info;\n>  \tchar *format = NULL;\n> +\tint allow_empty = 0;\n>  \tstruct option options[] = {\n>  \t\tOPT_STRING(0, \"format\", &format, N_(\"format\"),\n>  \t\t\t   N_(\"output format\")),\n> +\t\tOPT_BOOL(0, \"allow-empty\", &allow_empty,\n> +\t\t\t \"when set, it will use an empty set of fields if no field is requested\"),\n>  \t\tOPT_END()\n>  \t};\n>  \n> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\n> index 998c835795..db4a6aad17 100755\n> --- a/t/t1900-repo-info.sh\n> +++ b/t/t1900-repo-info.sh\n> @@ -15,11 +15,12 @@ test_lazy_prereq PERLJSON '\n>  '\n>  \n>  test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n> -\tgit repo-info --format=json >output &&\n> +\tgit repo-info --allow-empty --format=json >output &&\n>  \ttest_line_count = 2 output\n>  '\n> +\n>  test_expect_success 'plaintext: returns empty output with allow-empty' '\n> -\tgit repo-info --format=plaintext >output &&\n> +\tgit repo-info --allow-empty --format=plaintext >output &&\n>  \ttest_line_count = 0 output\n>  '\n"},{"id":"520522","messageId":"xmqqh60ayrqp.fsf@gitster.g","threadId":"63618","inReplyTo":"20250619225751.99699-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 5/7] repo-info: add the field references.format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T22:26:22Z","receivedAt":"2025-06-20T22:26:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add the field references.format to the repo-info command. The data\n> retrieved in this field is the same that currently is obtained by\n> running `git rev-parse --show-ref-format`.\n>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  builtin/repo-info.c  | 108 +++++++++++++++++++++++++++++++++++++++++--\n>  t/t1900-repo-info.sh |  58 +++++++++++++++++++++++\n>  2 files changed, 162 insertions(+), 4 deletions(-)\n>\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index 6499be0eae..6ce3e6134f 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -1,21 +1,56 @@\n>  #include \"builtin.h\"\n>  #include \"json-writer.h\"\n>  #include \"parse-options.h\"\n> +#include \"quote.h\"\n> +#include \"refs.h\"\n>  \n>  enum output_format {\n>  \tFORMAT_JSON,\n>  \tFORMAT_PLAINTEXT\n>  };\n>  \n> +enum repo_info_category {\n> +\tCATEGORY_REFERENCES = 1 << 0\n> +};\n> +\n> +enum repo_info_references_field {\n> +\tFIELD_REFERENCES_FORMAT = 1 << 0\n> +};\n> +\n> +struct repo_info_field {\n> +\tenum repo_info_category category;\n> +\tunion {\n> +\t\tenum repo_info_references_field references;\n> +\t} field;\n> +};\n\nAre we going to gain more than one union in this struct?  If not,\nname the union simply 'u', so that the accesses look like\n\"info.u.references\" instead of \"info.field.references\".  In that\nconstruct, a long name \".field\" does not add much information value\nanyway---the member in the union, like \"references\", is where all\nthe information value is.\n\n>  struct repo_info {\n>  \tstruct repository *repo;\n>  \tenum output_format format;\n> +\tint n_fields;\n> +\tstruct repo_info_field *fields;\n>  };\n>  \n> +static struct repo_info_field default_fields[] = {\n> +\t{\n> +\t\t.category = CATEGORY_REFERENCES,\n> +\t\t.field.references = FIELD_REFERENCES_FORMAT\n> +\t}\n> +};\n> +\n> +static void print_key_value(const char *key, const char *value) {\n> +\tprintf(\"%s=\", key);\n> +\tquote_c_style(value, NULL, stdout, 0);\n> +\tputchar('\\n');\n> +}\n> +\n>  static void repo_info_init(struct repo_info *repo_info,\n>  \t\t\t   struct repository *repo,\n> -\t\t\t   char *format)\n> +\t\t\t   char *format,\n> +\t\t\t   int allow_empty,\n> +\t\t\t   int argc, const char **argv)\n>  {\n> +\tint i;\n>  \trepo_info->repo = repo;\n>  \n>  \tif (format == NULL || !strcmp(format, \"json\"))\n> @@ -24,19 +59,83 @@ static void repo_info_init(struct repo_info *repo_info,\n>  \t\trepo_info->format = FORMAT_PLAINTEXT;\n>  \telse\n>  \t\tdie(\"invalid format %s\", format);\n> +\n> +\tif (argc == 0 && !allow_empty) {\n> +\t\trepo_info->n_fields = ARRAY_SIZE(default_fields);\n> +\t\trepo_info->fields = default_fields;\n> +\t} else {\n> +\t\trepo_info->n_fields = argc;\n> +\t\tALLOC_ARRAY(repo_info->fields, argc);\n> +\n> +\t\tfor (i = 0; i < argc; i++) {\n> +\t\t\tconst char *arg = argv[i];\n> +\t\t\tstruct repo_info_field *field = repo_info->fields + i;\n> +\n> +\t\t\tif (!strcmp(arg, \"references.format\")) {\n> +\t\t\t\tfield->category = CATEGORY_REFERENCES;\n> +\t\t\t\tfield->field.references = FIELD_REFERENCES_FORMAT;\n\nAre we going to have duplicate information between this\nif/elseif/... cascade and the default_fields[] array?\n\nThere needs a catalog of \"everything we support\" somewhere, which\ncan be used to determine what \"--all\" should show, whose entry can\nbe looked up with the textual name (like \"references.format\") for\nthe purpose of command line parsing.  Once you have such a\n\"everything we support\" table, this parser loop and \"--all\" handling\ncan become more table-driven.\n\n> -static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED)\n> +static void repo_info_release(struct repo_info *repo_info)\n>  {\n> +\tif (repo_info->fields != default_fields) free(repo_info->fields);\n\nDo not write if() and its body on the same line.\n\n>  }\n>  \n> -static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n> +static void repo_info_print_plaintext(struct repo_info *repo_info) {\n> +\tstruct repository *repo = repo_info->repo;\n> +\tint i;\n> +\tfor (i = 0; i < repo_info->n_fields; i++) {\n> +\t\tstruct repo_info_field *field = &repo_info->fields[i];\n> +\t\tswitch (field->category) {\n> +\t\tcase CATEGORY_REFERENCES:\n> +\t\t\tswitch (field->field.references) {\n> +\t\t\tcase FIELD_REFERENCES_FORMAT:\n> +\t\t\t\tprint_key_value(\"references.format\",\n> +\t\t\t\t\t\tref_storage_format_to_name(\n> +\t\t\t\t\t\t\trepo->ref_storage_format));\n> +\t\t\t\tbreak;\n> +\t\t\t}\n> +\t\t\tbreak;\n> +\t\t}\n\nCan't this also be made more table driven, by having a callback\nfunction pointer in the \"everything we support\" table?  That way,\nthis deeply nested loop does not have to be here and you do not have\nto chomp a line that is overly deeply nested in the middle just to\ngo under 80-column limit.\n\nIOW, the above code may become something like:\n\n\tfor (int i = 0; i < repo_info->n_fields; i++) {\n\t\tstruct repo_info_field *f = &repo_info->fields[i];\n                f->show(repo_info, f);\n\t}\n\nand the repo_info_field structure may have a pointer to the\nimplementation of \"show\" method for that particular type, which may\nlook like\n\n        static void show_references_format(struct repo_info *i,\n                                           struct repo_info_field *f)\n        {\n                struct repository *repo = i->repo;\n                enum ref_storage_format fmt = repo->ref_storage_format;\n                const char *name = ref_storage_format_to_name(fmt); \n\n                print_key_value(\"references.format\", name);\n        }\n\nHmm?\n\nI'll stop here for now.\n\nThanks.\n"},{"id":"520524","messageId":"xmqq4iwa0xzz.fsf@gitster.g","threadId":"63618","inReplyTo":"CAOLa=ZSgFYXEwdTyAfC2TSgVqpCSq6D1NDBwPU9cY-unX0Jx6Q@mail.gmail.com","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T23:55:12Z","receivedAt":"2025-06-20T23:55:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n>>  .gitignore          |  1 +\n>>  Makefile            |  1 +\n>>  builtin.h           |  1 +\n>>  builtin/repo-info.c | 21 +++++++++++++++++++++\n>>  git.c               |  1 +\n>>  meson.build         |  1 +\n>>  6 files changed, 26 insertions(+)\n>>  create mode 100644 builtin/repo-info.c\n>>\n>\n> Something I mentioned in the previous review, but hasn't been addressed\n> is the addition of documentation for the new command. With each patch\n> modifying the command, it would be also nice to add the respective\n> documentation.\n\nBsdies, the lack of Documentation/repo-info.adoc causes \"make check-docs\"\nfail.\n"},{"id":"520548","messageId":"BA2F0A3E-231B-4F0D-9485-912B90DC917F@gmail.com","threadId":"63618","inReplyTo":"CAOLa=ZQXeQX-v2JmKTpaJDLwWWKQSXoMUGbpmD6xx8KpbY91yw@mail.gmail.com","subject":"Re: [GSoC RFC PATCH v2 4/7] repo-info: add the --allow-empty flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-23T02:39:36Z","receivedAt":"2025-06-23T02:39:52Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> I still think this is early for this, and we should add this flag later\n> if the need arises.\n\nOk! I'll remove it in the next version.\n"},{"id":"520559","messageId":"CAOLa=ZSewCtgRnuLV1GPfLccFqcnNac1AT6vG8ghOquNqXsoZQ@mail.gmail.com","threadId":"63618","inReplyTo":"xmqq4iwa0xzz.fsf@gitster.g","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-23T09:19:01Z","receivedAt":"2025-06-23T09:19:05Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>>>  .gitignore          |  1 +\n>>>  Makefile            |  1 +\n>>>  builtin.h           |  1 +\n>>>  builtin/repo-info.c | 21 +++++++++++++++++++++\n>>>  git.c               |  1 +\n>>>  meson.build         |  1 +\n>>>  6 files changed, 26 insertions(+)\n>>>  create mode 100644 builtin/repo-info.c\n>>>\n>>\n>> Something I mentioned in the previous review, but hasn't been addressed\n>> is the addition of documentation for the new command. With each patch\n>> modifying the command, it would be also nice to add the respective\n>> documentation.\n>\n> Bsdies, the lack of Documentation/repo-info.adoc causes \"make check-docs\"\n> fail.\n\nMore so the reason, I didn't know the target also checks for missing\ndocumentation. That is great!\n"},{"id":"520560","messageId":"CAOLa=ZTCoc9vfeMrWxqU5psmbxGzW=B-QULeSR+uvF9kQi9WzQ@mail.gmail.com","threadId":"63618","inReplyTo":"xmqq1pre14ae.fsf@gitster.g","subject":"Re: [GSoC RFC PATCH v2 4/7] repo-info: add the --allow-empty flag","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-06-23T09:26:42Z","receivedAt":"2025-06-23T09:26:44Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n>\n>> Add a flag --allow-empty, which will force the output data to be empty\n>> when no field is requested.\n>\n> I still think we should flip the polarity of the option and give \"--all\"\n> to iterate over all variables, and by default show nothing.\n>\n\nI thought the idea was that we would show a subset of the fields by\ndefault when no user input is provided. Which would mean we have the\nfollowing:\n\n  * `git repo-info` which would show a subset of all fields, giving\n    important repository information.\n  * `git repo-info --all` which would show all available fields.\n  * `git repo-info <fields>` which would only show the requested fields.\n\nWith this context, I'm not entirely sure where '--allow-empty' fits in.\n\nIf this is/not the case, it would be nice to talk about this in more\ndetail in the cover message and also in the individual patches.\n\nThis is also a good time to think about if we should make the default to\nnot show anything as Junio mentioned.\n"},{"id":"520567","messageId":"af27af92-73d5-4f0a-84f4-9c91de6ab6e6@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 0/7] repo-info: add new command for retrieving repository info","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-06-23T13:42:08Z","receivedAt":"2025-06-23T13:42:11Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Lucas\n\nOn 19/06/2025 23:57, Lucas Seiki Oshiro wrote:\n> Hi!\n> \n> This is the second version of the repo-info RFC, and these are the main changes\n> introduced since v1:\n> \n> - The plaintext format now returns its fields in a key=value format\n\nI think that is a good idea. It also now quotes values that contain \ncontrol characters. In my experience quoting paths works well when one \nis processing the output of one git command and feeding into the stdin \nof another git command. In cases where the script itself wants to use \nthe path having to unquote them is a pain and it is much more convenient \nto to read NUL terminated input \"lines\" that are not quoted. I think the \noutput from this command falls into that latter category and so I'm not \nconvinced that a LF terminated output is particularly useful. I think \nusing an output format generated by 'printf(\"%s\\n%s\\0\", key, value)' \nwould be easier to parse. This format matches that used by 'git config \n--list -z'.\n\nAs this is a plumbing command I think it would be clearer if the caller \nwas required to specify the output format and the information that they \nrequire with an \"--all\" option for \"show me everything\" as Junio \nsuggested. If we were to set defaults for the format and keys now we \nwould be stuck with them forever.\n\nI've not seen any discussion of how paths are going to be encoded in the \nJSON output. As I understand it some JSON decoders only accept utf8 \ninput but the paths reported by git are arbitrary NUL terminated byte \nsequences. How is one expected to parse the output for a non utf8 \nencoded path using rust's JSON decoding for example?\n\nOn the subject of paths do you plan to support the equivalent of \"git \nrev-parse --git-path\"? That is very useful in scripts as the script \nauthor does not have to worry about the details of where exactly each \nfile lives and for paths like \"index\" or \"hooks\" it takes the relevant \nenvironment variable or config setting into account. We should design \nthe output format so that it can accommodate keys with the requested \npath. I think it would be reasonable to reject requests for paths \ncontaining newlines which would be compatible with the output format \nsuggested above.\n\nI'm not sure what the future plans for this command are but when I'm \nscripting around git it would be nice to be able to a single process \nthat I could query for the things currently returned by \"git rev-parse\", \n\"git var\" and \"git config\"\n\nBest Wishes\n\nPhillip\n\n"},{"id":"520583","messageId":"652FDA35-C20D-4F27-A22A-025CA08EB013@gmail.com","threadId":"63618","inReplyTo":"af27af92-73d5-4f0a-84f4-9c91de6ab6e6@gmail.com","subject":"Re: [GSoC RFC PATCH v2 0/7] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-23T18:49:20Z","receivedAt":"2025-06-23T18:49:37Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Hi Lucas\n\nHi, Phillip, thanks for joining this discussion!\n\n> I think using an output format generated by 'printf(\"%s\\n%s\\0\", key,\n> value)' would be easier to parse. This format matches that used by 'git\n> config --list -z'.\n\nThanks for your suggestion! However, this still breaks in the corner case \nmentioned by Junio in \nhttps://lore.kernel.org/git/xmqqikl3mtx2.fsf@gitster.g/:\nwhen a value contains a LF, which would be possible to have in the (yet to be\nimplemented) path values.\n\n> I've not seen any discussion of how paths are going to be encoded in the\n> JSON output. As I understand it some JSON decoders only accept utf8 input\n> but the paths reported by git are arbitrary NUL terminated byte sequences.\n> How is one expected to parse the output for a non utf8 encoded path using\n> rust's JSON decoding for example?\n\nBy now, I'm directly using the jw_* functions, which format strings using the\nfunction append_quoted_string, introduced in 75459410ed (json_writer: new\nroutines to create JSON data, 2018-07-13). It was also discussed when that\nfunction was introduced:\n\n\"\"\"\n    We say \"JSON-like\" because we do not enforce the Unicode (usually UTF-8)\n    requirement on string fields.  Internally, Git does not necessarily have\n    Unicode/UTF-8 data for most fields, so it is currently unclear the best\n    way to enforce that requirement.  For example, on Linux pathnames can\n    contain arbitrary 8-bit character data, so a command like \"status\" would\n    not know how to encode the reported pathnames.  We may want to revisit\n    this (or double encode such strings) in the future.\n\"\"\"\n\nSo, it looks like that \"the future\" is soon :-). In this RFC, I'm not handling\npaths yet, and I can't propose a proper solution by now as I honestly know\nvery little about UTF-8 encoding... \n\nThe first solution that I can think of is to check if the sequence is a valid\nUTF-8 bytestring, aborting the entire command if it's not, which would be\nbetter than just guess the charset and re-encode it as UTF-8. However,\nI don't know how hard it would be to do.\n\n> On the subject of paths do you plan to support the equivalent of \"git\n> rev-parse --git-path\"?\n\nHmmmm... In the way that it works under rev-parse, no, as it may bloat this\ncommand with other things that aren't exactly metadata.\n\n> I'm not sure what the future plans for this command are but when I'm\n> scripting around git it would be nice to be able to a single process that I\n> could query for the things currently returned by \"git rev-parse\", \"git var\"\n> and \"git config\"\n\nMy concern here is that this main motivation for this new command is that\nrev-parse has too many responsibilities. Giving too many responsibilities to\nthis new command may turn it into a new rev-parse and create a XKCD 927 [1]\nsituation\n\n> \n> Best Wishes\n> \n> Phillip\n> \n\nThanks again for bringing more light to this discussion! These first patches\nare only outputting hardcoded strings from Git, and dealing with Unicode is\nsomething that I'll really need to think about how to solve.\n\n[1] https://xkcd.com/927/"},{"id":"520584","messageId":"036552C3-5448-4CB6-83A1-9504D29F62AF@gmail.com","threadId":"63618","inReplyTo":"CAOLa=ZSgFYXEwdTyAfC2TSgVqpCSq6D1NDBwPU9cY-unX0Jx6Q@mail.gmail.com","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-23T19:04:27Z","receivedAt":"2025-06-23T19:04:41Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\n> Something I mentioned in the previous review, but hasn't been addressed\n> is the addition of documentation for the new command.\n\nI haven't wrote a documentation because I thought it would be too early\nas this command is still under discussion, but it looks like it is time\nto starting working on it.\n\nI'll include that in v3.\n\nThanks!\n\n"},{"id":"520589","messageId":"9E8D7078-25B8-4036-8961-A1758C17FBDC@gmail.com","threadId":"63618","inReplyTo":"CAOLa=ZTCoc9vfeMrWxqU5psmbxGzW=B-QULeSR+uvF9kQi9WzQ@mail.gmail.com","subject":"Re: [GSoC RFC PATCH v2 4/7] repo-info: add the --allow-empty flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-06-23T20:28:21Z","receivedAt":"2025-06-23T20:28:36Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> I thought the idea was that we would show a subset of the fields by\n> default when no user input is provided. Which would mean we have the\n> following:\n> \n>  * `git repo-info` which would show a subset of all fields, giving\n>    important repository information.\n>  * `git repo-info --all` which would show all available fields.\n>  * `git repo-info <fields>` which would only show the requested fields.\n> \n> \n> This is also a good time to think about if we should make the default to\n> not show anything as Junio mentioned.\n\nAfter this review, I'm starting to think that leaving it empty by default\nwould be better. Specially after the review by Phillip Wood [1], who\nhas a good argument for it:\n\n\"\"\"\n  As this is a plumbing command I think it would be clearer if the caller \n  was required to specify the output format and the information that they \n  require with an \"--all\" option for \"show me everything\" as Junio \n  suggested. If we were to set defaults for the format and keys now we \n  would be stuck with them forever.\n\"\"\"\n\n\n[1] https://lore.kernel.org/git/af27af92-73d5-4f0a-84f4-9c91de6ab6e6@gmail.com/\n\n"},{"id":"520636","messageId":"223c7cbd-610e-49e2-90e2-5914cbc0f1d7@gmail.com","threadId":"63618","inReplyTo":"652FDA35-C20D-4F27-A22A-025CA08EB013@gmail.com","subject":"Re: [GSoC RFC PATCH v2 0/7] repo-info: add new command for retrieving repository info","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-06-24T13:03:57Z","receivedAt":"2025-06-24T13:04:02Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Lucas\n\nOn 23/06/2025 19:49, Lucas Seiki Oshiro wrote:\n> \n>> I think using an output format generated by 'printf(\"%s\\n%s\\0\", key,\n>> value)' would be easier to parse. This format matches that used by 'git\n>> config --list -z'.\n> \n> Thanks for your suggestion! However, this still breaks in the corner case\n> mentioned by Junio in\n> https://lore.kernel.org/git/xmqqikl3mtx2.fsf@gitster.g/:\n> when a value contains a LF, which would be possible to have in the (yet to be\n> implemented) path values.\n\nThe reason git uses NUL termination for other commands is to prevent \nbreaking the output when values contain newlines. The output format I'm \nsuggesting is\n\n     <key><LF><value><NUL>\n\nso the output for \"path.git-dir\" written as a C string would be\n\n     \"path.git-dir\\n/home/phil/src/git/.git\\0\"\n\nThe value can safely contain newlines because it is terminated by '\\0'. \nThe reason that \"git config --list -z\" exists is to provide an \nunambiguous output format as config values can contain newlines.\n\n>> I've not seen any discussion of how paths are going to be encoded in the\n>> JSON output. As I understand it some JSON decoders only accept utf8 input\n>> but the paths reported by git are arbitrary NUL terminated byte sequences.\n>> How is one expected to parse the output for a non utf8 encoded path using\n>> rust's JSON decoding for example?\n> \n> [...]>\n> The first solution that I can think of is to check if the sequence is a valid\n> UTF-8 bytestring, aborting the entire command if it's not, which would be\n> better than just guess the charset and re-encode it as UTF-8. However,\n> I don't know how hard it would be to do.\n\nI'm far from an expert but I think the normal solution is to base64 \nencode bytestrings so the caller can get the original bytes back. We'd \nneed to do this for all paths. Even if we could reliably guess the \nencoding (which I'm not sure we can) and re-encode it as utf-8 the \ncaller wouldn't know if the path was really utf-8 or if it had been \nre-encoded and they needed to convert it back to the original encoding \nto use it.\n\n>> On the subject of paths do you plan to support the equivalent of \"git\n>> rev-parse --git-path\"?\n> \n> Hmmmm... In the way that it works under rev-parse, no, as it may bloat this\n> command with other things that aren't exactly metadata.\n\nThat's a shame as I think it we should be encouraging users to use \"git \nrev-parse --git-path\" rather than building their own paths using \"git \nrev-parse --git-dir\". The latter is easy to get wrong for example \nassuming the index resides at \"$GIT_DIR/index\" when \"$GIT_INDEX_FILE\" is \nset or running a command from a worktree and assuming the path is under \n\"$GIT_DIR\" when it actually resides under \"$GIT_COMMON_DIR\". If this \ncommand is going to return \"$GIT_DIR\" and \"$GIT_WORK_TREE\" then I don't \nsee why it should not be able to provide other paths.\n\n>> I'm not sure what the future plans for this command are but when I'm\n>> scripting around git it would be nice to be able to a single process that I\n>> could query for the things currently returned by \"git rev-parse\", \"git var\"\n>> and \"git config\"\n> \n> My concern here is that this main motivation for this new command is that\n> rev-parse has too many responsibilities. Giving too many responsibilities to\n> this new command may turn it into a new rev-parse and create a XKCD 927 [1]\n> situation\n\nI should have been clearer that I was talking about the path and \nrepository information options of \"git rev-parse\". Those combined with \n\"git var\" and \"git config\" are all repository settings. Having a unified \ninterface to them would be an improvement on the status quo where users \nhave to know which command to call to query different settings. There \nwould be a clear focus on returning repository settings, which I think \nis very different from \"git rev-parse\" that combines revision parsing, \ncommand line parsing, shell quoting and repository information. I don't \nthink you necessarily need to implement them as part of this project but \nwe should design the input and output formats so that the command can be \nextended in the future.\n\nBest Wishes\n\nPhillip\n"},{"id":"520639","messageId":"xmqqjz512r2l.fsf@gitster.g","threadId":"63618","inReplyTo":"223c7cbd-610e-49e2-90e2-5914cbc0f1d7@gmail.com","subject":"Re: [GSoC RFC PATCH v2 0/7] repo-info: add new command for retrieving repository info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-24T13:43:14Z","receivedAt":"2025-06-24T13:43:17Z","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> The reason git uses NUL termination for other commands is to prevent\n> breaking the output when values contain newlines. The output format\n> I'm suggesting is\n>\n>     <key><LF><value><NUL>\n>\n> so the output for \"path.git-dir\" written as a C string would be\n>\n>     \"path.git-dir\\n/home/phil/src/git/.git\\0\"\n>\n> The value can safely contain newlines because it is terminated by\n> '\\0'. The reason that \"git config --list -z\" exists is to provide an\n> unambiguous output format as config values can contain newlines.\n\nFor Lukas's purpose, I presume we are in full control of keys, and\ncan assume that keys do not have any LF in them, so it is safe to\ntreat everything after the first LF up to the NUL as its value.  \n\nSimilarly, even though \"config\" allows essentially any byte value in\nits second level component name in three component variable names,\nwe do not allow LF or NUL in it, so we are safe there too.\n\nThanks.\n\n\n\n\n"},{"id":"520640","messageId":"254e4819-a693-4fb7-aa92-260038cbfbe2@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 5/7] repo-info: add the field references.format","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-06-24T14:03:01Z","receivedAt":"2025-06-24T14:03:06Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Lucas\n\nOn 19/06/2025 23:57, Lucas Seiki Oshiro wrote:\n> Add the field references.format to the repo-info command. The data\n> retrieved in this field is the same that currently is obtained by\n> running `git rev-parse --show-ref-format`.\n> \n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n\nI've concentrated my comments on the tests as others have commented on \nthe code itself. In general test bodies should be wrapped in single \nquotes rather than double quotes and one should prefer test_cmp() over \ntest_line_count().\n\n> +# Test if a field is correctly returned in both plaintext and json formats.\n> +#\n> +# Usage: test_repo_info <label> <init command> <key> <expected value>\n> +#\n> +# Arguments:\n> +#   label: the label of the test\n> +#   init command: a command that creates a repository called 'repo', configured\n> +#      accordingly to what is being tested\n> +#   key: the key of the field that is being tested\n> +#   expected value: the value that the field should contain\n> +test_repo_info () {\n> +        label=$1\n> +        init_command=$2\n> +        key=$3\n> +        expected_value=$4\n> +\n> +        test_expect_success PERLJSON \"json: $label\" \"\n\nThis double quote should be a single quote. Unlike the test test title, \nthe body is passed to eval so there is no need to use double quotes to \nexpand shell variables. Indeed doing so is counter productive as it \nmeans we pass the result of the variable expansion to eval rather that \nthe variable name.\n\n> +                test_when_finished 'rm -rf repo' &&\n> +                '$SHELL_PATH' -c '$init_command' &&\n\nThere is no need to fork a separate shell process here, you can use\n\n     eval \"$init_command\"\n\ninstead.\n\n> +                cd repo &&\n\nIf you change directory in a test then you must do so in a subshell so \nthat we return to the original directory when the test finishes. In this \ncase you're only running a single command in repo so you can use\n\n     git -C repo repo-info ...\n\ninstead and avoid using \"cd\" all together.\n\n> +                echo '$expected_value' >expect &&\n> +                git repo-info '$key' >output &&\n> +                cat output | parse_json >parsed &&\n> +                grep -F 'row[0].$key' parsed | cut -d ' ' -f 2 >value &&\n> +                cat value | sed 's/^0$/false/' | sed 's/^1$/true/' >actual &&\n\nsed accepts filenames so there is no need to use \"cat\" here. It also \naccepts multiple expressions so you only need a single command\n\n     sed \"s/^0\\$/false/; s/^1\\$/true/\" value >actual &&\n\n> +                test_cmp expect actual\n> +        \"\n\nPutting all of the above together the test looks like\n\n\ttest_expect_success PERLJSON \"json: $label\" '\n\t\ttest_when_finished \"rm -rf repo\" &&\n\t\teval \"$init_command\" &&\n\t\techo \"$expected_value\" >expect &&\n\t\tgit -C repo repo-info \"$key\" >output &&\n\t\tcat output | parse_json >parsed &&\n\t\tgrep -F \"row[0].$key\" parsed | cut -d \" \" -f 2 >value &&\n\t\tsed \"s/^0\\$/false/; s/^1\\$/true/\" value >actual &&\n\t\ttest_cmp expect actual\n\t'\n\n> +        test_expect_success \"plaintext: $label\" \"\n> +                test_when_finished 'rm -rf repo' &&\n> +                '$SHELL_PATH' -c '$init_command' &&\n> +                cd repo &&\n> +                echo '$expected_value' >expect &&\n> +                git repo-info --format=plaintext '$key' >output &&\n> +                cat output | cut -d '=' -f 2 >actual &&\n> +                test_cmp expect actual\n> +        \"\n\nMy comments above apply here as well.\n\n> [...]\n> +test_expect_success 'plaintext: output all default fields' \"\n\nThe body should be single quoted as it is eval'd\n\n> +\tgit repo-info --format=plaintext >actual &&\n> +\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\ntest_line_count is a pretty weak assertion. It would be better to use \ntest_cmp()\n\n\tgit repo-info --format=plaintext >actual &&\n\tsort actual >actual.sorted &&\n\tcat >expect <<-\\EOF &&\n\t<EXPECTED OUTPUT>\n\tEOF\n\ttest_cmp expect actual.sorted\n\n> +\"\n> +\n> +test_expect_success PERLJSON 'json: output all default fields' \"\n> +\tgit repo-info --format=json > output &&\n> +\tcat output | parse_json | grep '.*\\..*\\..*' >actual &&\n\nYou do not need to use \"cat\" here as you can redirect the standard input \nwhen you run parse_json.\n\n\tparse_json <output | grep ...\n\n> +\ttest_line_count = $DEFAULT_NUMBER_OF_FIELDS actual\nThis is an even weaker assertion as the number of lines in a json file \nis not related to its content. Ideally we should normalize the json \noutput into a standard format and use test_cmp. I don't know how \npractical that is.\n\nBest Wishes\n\nPhillip\n\n"},{"id":"520641","messageId":"f50c6e4d-f789-4779-a8de-288b7341283f@gmail.com","threadId":"63618","inReplyTo":"20250619225751.99699-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-06-24T14:03:18Z","receivedAt":"2025-06-24T14:03:23Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Lucas\n\nOn 19/06/2025 23:57, Lucas Seiki Oshiro wrote:\n> Create a new Git subcommand called repo-info. `git repo-info` will query\n> metadata from the current repository and outputs it as JSON or plaintext.\n> \n> Also add entries for this new command in:\n> \n> - the build files (Makefile and meson.build)\n> - builtin.h\n> - git.c\n> - .gitignore\n> \n> In option parsing, use PARSE_OPT_KEEP_UNKNOWN_OPT to allow the users\n> specify after the flags the information that they want to retrieve.\n\nThat should not necessary, the option parser will keep any arguments \nthat do not begin with \"-\" without this flag. Using \nPARSE_OPT_KEEP_UNKNOWN_OPT is intended for accepting options that are \npassed to another command and prevents the option parser from \ncomplaining about unknown options which is not what we want here.\n\nBest Wishes\n\nPhillip\n\n> \n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>   .gitignore          |  1 +\n>   Makefile            |  1 +\n>   builtin.h           |  1 +\n>   builtin/repo-info.c | 21 +++++++++++++++++++++\n>   git.c               |  1 +\n>   meson.build         |  1 +\n>   6 files changed, 26 insertions(+)\n>   create mode 100644 builtin/repo-info.c\n> \n> diff --git a/.gitignore b/.gitignore\n> index 04c444404e..b2f3fb0047 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -139,6 +139,7 @@\n>   /git-repack\n>   /git-replace\n>   /git-replay\n> +/git-repo-info\n>   /git-request-pull\n>   /git-rerere\n>   /git-reset\n> diff --git a/Makefile b/Makefile\n> index 70d1543b6b..50e3a3cbcc 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -1308,6 +1308,7 @@ BUILTIN_OBJS += builtin/remote.o\n>   BUILTIN_OBJS += builtin/repack.o\n>   BUILTIN_OBJS += builtin/replace.o\n>   BUILTIN_OBJS += builtin/replay.o\n> +BUILTIN_OBJS += builtin/repo-info.o\n>   BUILTIN_OBJS += builtin/rerere.o\n>   BUILTIN_OBJS += builtin/reset.o\n>   BUILTIN_OBJS += builtin/rev-list.o\n> diff --git a/builtin.h b/builtin.h\n> index bff13e3069..cc6bc95962 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n>   int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n>   int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n>   int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n> +int cmd_repo_info(int argc, const char **argv, const char *prefix, struct repository *repo);\n>   int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n>   int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n>   int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> new file mode 100644\n> index 0000000000..a5c43e253f\n> --- /dev/null\n> +++ b/builtin/repo-info.c\n> @@ -0,0 +1,21 @@\n> +#include \"builtin.h\"\n> +#include \"parse-options.h\"\n> +\n> +int cmd_repo_info(int argc,\n> +\t\t  const char **argv,\n> +\t\t  const char *prefix,\n> +\t\t  struct repository *repo UNUSED)\n> +{\n> +\tconst char *const repo_info_usage[] = {\n> +\t\t\"git repo-info\",\n> +\t\tNULL\n> +\t};\n> +\tstruct option options[] = {\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n> +\t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n> +\n> +\treturn 0;\n> +}\n> diff --git a/git.c b/git.c\n> index 07a5fe39fb..27a2b3569b 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n>   \t{ \"repack\", cmd_repack, RUN_SETUP },\n>   \t{ \"replace\", cmd_replace, RUN_SETUP },\n>   \t{ \"replay\", cmd_replay, RUN_SETUP },\n> +\t{ \"repo-info\", cmd_repo_info, RUN_SETUP },\n>   \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n>   \t{ \"reset\", cmd_reset, RUN_SETUP },\n>   \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\n> diff --git a/meson.build b/meson.build\n> index 7fea4a34d6..06f2f647ba 100644\n> --- a/meson.build\n> +++ b/meson.build\n> @@ -645,6 +645,7 @@ builtin_sources = [\n>     'builtin/repack.c',\n>     'builtin/replace.c',\n>     'builtin/replay.c',\n> +  'builtin/repo-info.c',\n>     'builtin/rerere.c',\n>     'builtin/reset.c',\n>     'builtin/rev-list.c',\n\n"},{"id":"520647","messageId":"xmqq8qlh2mc0.fsf@gitster.g","threadId":"63618","inReplyTo":"254e4819-a693-4fb7-aa92-260038cbfbe2@gmail.com","subject":"Re: [GSoC RFC PATCH v2 5/7] repo-info: add the field references.format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-24T15:25:35Z","receivedAt":"2025-06-24T15:25:38Z","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've concentrated my comments on the tests as others have commented on\n> the code itself. In general test bodies should be wrapped in single\n> quotes rather than double quotes and one should prefer test_cmp() over\n> test_line_count().\n\n>> +                echo '$expected_value' >expect &&\n>> +                git repo-info '$key' >output &&\n>> +                cat output | parse_json >parsed &&\n\nRunning \"cat\" on a single file and piping it to anything is an\nanti-pattern.  The fact that you can pipe output into the downstream\ncommand means that the downstream command is prepared to read from\nits standard input, so\n\n\tparse_json <output >parsed &&\n\nshould be sufficient, right?\n\n>> +                grep -F 'row[0].$key' parsed | cut -d ' ' -f 2 >value &&\n>> +                cat value | sed 's/^0$/false/' | sed 's/^1$/true/' >actual &&\n>\n> sed accepts filenames so there is no need to use \"cat\" here. It also\n> accepts multiple expressions so you only need a single command\n>\n>     sed \"s/^0\\$/false/; s/^1\\$/true/\" value >actual &&\n\nAnd you probably do not even need grep piped into cut either, as sed\nis a powerful enough language.  We can also cheat a bit by taking\nadvantage of the fact that the characters used in keys are fairly\ntightly controlled, so perhaps something along this line?\n\n\tsed -n -e \"/row[0].$key/{\n\t\ts/^[^ ]* //\n                s/^1\\$/true/\n\t\ts/^0\\$/false/\n                p;\n\t}\" parsed >actual &&\n\n> Best Wishes\n>\n> Phillip\n\nThanks.\n"},{"id":"520677","messageId":"e892fe89-045f-4220-8436-2d536f10e1a2@gmail.com","threadId":"63618","inReplyTo":"xmqq8qlh2mc0.fsf@gitster.g","subject":"Re: [GSoC RFC PATCH v2 5/7] repo-info: add the field references.format","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-06-25T08:40:22Z","receivedAt":"2025-06-25T08:40:26Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 24/06/2025 16:25, Junio C Hamano wrote:\n > >>> +                echo '$expected_value' >expect &&\n>>> +                git repo-info '$key' >output &&\n>>> +                cat output | parse_json >parsed &&\n> \n> Running \"cat\" on a single file and piping it to anything is an\n> anti-pattern.  The fact that you can pipe output into the downstream\n> command means that the downstream command is prepared to read from\n> its standard input, so\n> \n> \tparse_json <output >parsed &&\n> \n> should be sufficient, right?\n\nGood catch, I commented on this in a test below this one but missed it here.\n\n>>> +                grep -F 'row[0].$key' parsed | cut -d ' ' -f 2 >value &&\n>>> +                cat value | sed 's/^0$/false/' | sed 's/^1$/true/' >actual &&\n>>\n>> sed accepts filenames so there is no need to use \"cat\" here. It also\n>> accepts multiple expressions so you only need a single command\n>>\n>>      sed \"s/^0\\$/false/; s/^1\\$/true/\" value >actual &&\n> \n> And you probably do not even need grep piped into cut either, as sed\n> is a powerful enough language.  We can also cheat a bit by taking\n> advantage of the fact that the characters used in keys are fairly\n> tightly controlled, so perhaps something along this line?\n> \n> \tsed -n -e \"/row[0].$key/{\n> \t\ts/^[^ ]* //\n>                  s/^1\\$/true/\n> \t\ts/^0\\$/false/\n>                  p;\n> \t}\" parsed >actual &&\n\nNice, as well as saving a couple of processes this avoids truncating \nvalues that contain a space.\n\nThanks\n\nPhillip\n\n"},{"id":"521127","messageId":"EB2B7A97-B9C4-4F7E-BD27-688C6303DA1C@gmail.com","threadId":"63618","inReplyTo":"223c7cbd-610e-49e2-90e2-5914cbc0f1d7@gmail.com","subject":"Re: [GSoC RFC PATCH v2 0/7] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-01T22:18:07Z","receivedAt":"2025-07-01T22:18:22Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> The reason git uses NUL termination for other commands is to prevent breaking the output when values contain newlines. The output format I'm suggesting is\n> \n>    <key><LF><value><NUL>\n> \n> so the output for \"path.git-dir\" written as a C string would be\n> \n>    \"path.git-dir\\n/home/phil/src/git/.git\\0\"\n> \n> The value can safely contain newlines because it is terminated by '\\0'. The reason that \"git config --list -z\" exists is to provide an unambiguous output format as config values can contain newlines.\n\nOK!\n\nI can't see any downsides in this format. I'll use it in v3!\n\n> If this command is going to return \"$GIT_DIR\" and \"$GIT_WORK_TREE\" then I don't see why it should not be able to provide other paths.\n\nHmmm... I never used `git rev-parse --git-path`, but after inspecting it,\nit seems to have special cases for:\n\n- grafts file\n- index file\n- objects directory\n- hooks directory\n\nWhat about adding them all to the `git repo-info` under the `path` category?\nCurrently, these are the fields that I plan to add:\n\n- git-dir\n- common-dir\n- toplevel-dir\n- superproject-working-tree\n\n> Those combined with \"git var\" and \"git config\" are all repository settings. Having a unified interface to them would be an improvement on the status quo where users have to know which command to call to query different settings.\n\nFair! By now, I'm focusing on rev-parse, but it seems to make sense.\n\n> Best Wishes\n\nThanks again for your extensive review!\n\n"},{"id":"521152","messageId":"d61879e2-a93e-42ad-bd16-7a21b9819dbb@gmail.com","threadId":"63618","inReplyTo":"EB2B7A97-B9C4-4F7E-BD27-688C6303DA1C@gmail.com","subject":"Re: [GSoC RFC PATCH v2 0/7] repo-info: add new command for retrieving repository info","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-07-02T09:10:08Z","receivedAt":"2025-07-02T09:10:11Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Lucas\n\nOn 01/07/2025 23:18, Lucas Seiki Oshiro wrote:\n> \n>> The reason git uses NUL termination for other commands is to prevent breaking the output when values contain newlines. The output format I'm suggesting is\n>>\n>>     <key><LF><value><NUL>\n>>\n>> so the output for \"path.git-dir\" written as a C string would be\n>>\n>>     \"path.git-dir\\n/home/phil/src/git/.git\\0\"\n>>\n>> The value can safely contain newlines because it is terminated by '\\0'. The reason that \"git config --list -z\" exists is to provide an unambiguous output format as config values can contain newlines.\n> \n> I can't see any downsides in this format. I'll use it in v3!\n\nThat's great\n\n> Hmmm... I never used `git rev-parse --git-path`, but after inspecting it,\n> it seems to have special cases for:\n> \n> - grafts file\n> - index file\n> - objects directory\n> - hooks directory\n\nand all the paths that are adjusted by update_common_dir() as well.\n\n> What about adding them all to the `git repo-info` under the `path` category?\n\nThat means keeping a list of paths that are special cased in two \nseparate places which means they'll almost certainly get out of sync at \nsome point in the future. It also means that instead of querying a whole \npath of a hook the script author has to remember to look up the hooks \npath and then append the hook name to that which makes it more of a faff \nto use. Letting the user just query the path they are interested in is a \nmuch nicer interface and makes it much easier to deal with the paths \nthat reside under $GIT_COMMON_DIR.\n\n> Currently, these are the fields that I plan to add:\n> \n> - git-dir\n> - common-dir\n> - toplevel-dir\n> - superproject-working-tree\n\ngit-prefix would be useful as well I think.\n\nThanks\n\nPhillip\n\n"},{"id":"521258","messageId":"aGZqIJe2K3jGYeI3@pks.im","threadId":"63618","inReplyTo":"20250619225751.99699-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 2/7] repo-info: add the --format flag","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-03T11:31:44Z","receivedAt":"2025-07-03T11:31:56Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jun 19, 2025 at 07:57:46PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index a5c43e253f..cbe1475e30 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -1,21 +1,73 @@\n>  #include \"builtin.h\"\n> +#include \"json-writer.h\"\n>  #include \"parse-options.h\"\n>  \n> +enum output_format {\n> +\tFORMAT_JSON\n> +};\n> +\n> +struct repo_info {\n> +\tstruct repository *repo;\n> +\tenum output_format format;\n> +};\n> +\n> +static void repo_info_init(struct repo_info *repo_info,\n> +\t\t\t   struct repository *repo,\n> +\t\t\t   char *format)\n> +{\n> +\trepo_info->repo = repo;\n> +\n> +\tif (format == NULL || !strcmp(format, \"json\"))\n\nNit: we typically don't compare to NULL explicitly.\n\n\tif (!format || !strcmp(format, \"json\"))\n\n> +\t\trepo_info->format = FORMAT_JSON;\n> +\telse\n> +\t\tdie(\"invalid format %s\", format);\n> +}\n> +\n> +static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n> +{\n> +\tstruct json_writer jw;\n> +\n> +\tjw_init(&jw);\n> +\n> +\tjw_object_begin(&jw, 1);\n> +\tjw_end(&jw);\n> +\n> +\tputs(jw.json.buf);\n> +\tjw_release(&jw);\n> +}\n> +\n> +static void repo_info_print(struct repo_info *repo_info)\n> +{\n> +\tenum output_format format = repo_info->format;\n\nThis variable indirection feels a bit pointless.\n\n> +\tswitch (format) {\n> +\tcase FORMAT_JSON:\n> +\t\trepo_info_print_json(repo_info);\n> +\t\tbreak;\n\nWe should probably `BUG()` in case there's any other unknown format\nvalue stored in `repo_info`.\n\n> +\t}\n> +}\n> +\n>  int cmd_repo_info(int argc,\n>  \t\t  const char **argv,\n>  \t\t  const char *prefix,\n> -\t\t  struct repository *repo UNUSED)\n> +\t\t  struct repository *repo)\n>  {\n>  \tconst char *const repo_info_usage[] = {\n>  \t\t\"git repo-info\",\n>  \t\tNULL\n>  \t};\n> +\tstruct repo_info repo_info;\n> +\tchar *format = NULL;\n\nThis should be const.\n\n>  \tstruct option options[] = {\n> +\t\tOPT_STRING(0, \"format\", &format, N_(\"format\"),\n> +\t\t\t   N_(\"output format\")),\n>  \t\tOPT_END()\n>  \t};\n>  \n>  \targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n>  \t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n> +\trepo_info_init(&repo_info, repo, format);\n> +\trepo_info_print(&repo_info);\n>  \n>  \treturn 0;\n>  }\n> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\n> new file mode 100755\n> index 0000000000..f634e1a285\n> --- /dev/null\n> +++ b/t/t1900-repo-info.sh\n> @@ -0,0 +1,22 @@\n> +#!/bin/sh\n> +\n> +test_description='test git repo-info'\n> +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n\nWe should avoid setting the default initial branch name in new tests.\nThe variable really only exists for legacy tests where we assume that\nthe branch has a specific name.\n\n> +. ./test-lib.sh\n> +\n> +parse_json () {\n> +\ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n> +}\n> +\n> +test_lazy_prereq PERLJSON '\n> +\tperl -MJSON -e \"exit 0\"\n> +'\n> +\n> +test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n> +\tgit repo-info --format=json >output &&\n> +\ttest_line_count = 2 output\n\nThis test doesn't really test much. In any case, two lines of output\ncertainly isn't empty output from my perspective. We should verify that\nthe generated output really is the empty JSON object.\n\nPatrick\n"},{"id":"521259","messageId":"aGZqK5eBA18vHAa_@pks.im","threadId":"63618","inReplyTo":"20250619225751.99699-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-03T11:31:55Z","receivedAt":"2025-07-03T11:32:04Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jun 19, 2025 at 07:57:45PM -0300, Lucas Seiki Oshiro wrote:\n> Create a new Git subcommand called repo-info. `git repo-info` will query\n> metadata from the current repository and outputs it as JSON or plaintext.\n\nOne thing I wondered: Justin is currently iterating on git-survey(1),\nwhich is the command Stolee proposed a while ago to gather repository\nmetrics. Would it make sense to maybe have such whole-repo commands\ngrouped together in a `git repo` top-level command? E.g. `git repo info`\nfor your command, `git repo size` to gather information about the repo\nsize.\n\n> Also add entries for this new command in:\n> \n> - the build files (Makefile and meson.build)\n> - builtin.h\n> - git.c\n> - .gitignore\n> \n> In option parsing, use PARSE_OPT_KEEP_UNKNOWN_OPT to allow the users\n> specify after the flags the information that they want to retrieve.\n> \n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by Patrick Steinhardt <ps@pks.im>\n\nNit: missing colon. It's also present in subsequent patches.\n\nPatrick\n"},{"id":"521260","messageId":"aGZqMZm2Ttngty8x@pks.im","threadId":"63618","inReplyTo":"20250619225751.99699-4-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 3/7] repo-info: add plaintext as an output format","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-03T11:32:01Z","receivedAt":"2025-07-03T11:32:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jun 19, 2025 at 07:57:47PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\n> index f634e1a285..998c835795 100755\n> --- a/t/t1900-repo-info.sh\n> +++ b/t/t1900-repo-info.sh\n> @@ -18,5 +18,9 @@ test_expect_success PERLJSON 'json: returns empty output with allow-empty' '\n>  \tgit repo-info --format=json >output &&\n>  \ttest_line_count = 2 output\n>  '\n\nTests should be separated from one another by an empty line.\n\n> +test_expect_success 'plaintext: returns empty output with allow-empty' '\n> +\tgit repo-info --format=plaintext >output &&\n> +\ttest_line_count = 0 output\n> +'\n\nPatrick\n"},{"id":"521261","messageId":"aGZqN-oqctJ79Chz@pks.im","threadId":"63618","inReplyTo":"20250619225751.99699-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 5/7] repo-info: add the field references.format","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-03T11:32:07Z","receivedAt":"2025-07-03T11:32:14Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jun 19, 2025 at 07:57:49PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index 6499be0eae..6ce3e6134f 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -1,21 +1,56 @@\n>  #include \"builtin.h\"\n>  #include \"json-writer.h\"\n>  #include \"parse-options.h\"\n> +#include \"quote.h\"\n> +#include \"refs.h\"\n>  \n>  enum output_format {\n>  \tFORMAT_JSON,\n>  \tFORMAT_PLAINTEXT\n>  };\n>  \n> +enum repo_info_category {\n> +\tCATEGORY_REFERENCES = 1 << 0\n> +};\n> +\n> +enum repo_info_references_field {\n> +\tFIELD_REFERENCES_FORMAT = 1 << 0\n> +};\n\nMissing commas after both enum values.\n\n> +struct repo_info_field {\n> +\tenum repo_info_category category;\n> +\tunion {\n> +\t\tenum repo_info_references_field references;\n> +\t} field;\n> +};\n\nHm, okay. I guess later patches will add separate enums for each\ncategory, so this saves us a couple bytes as the number of categories\ngrows.\n\n>  struct repo_info {\n>  \tstruct repository *repo;\n>  \tenum output_format format;\n> +\tint n_fields;\n\nThis should be `size_t`. Also, it's typical in our code base to call\nthis `fields_nr`.\n\n> +\tstruct repo_info_field *fields;\n>  };\n>  \n> +static struct repo_info_field default_fields[] = {\n> +\t{\n> +\t\t.category = CATEGORY_REFERENCES,\n> +\t\t.field.references = FIELD_REFERENCES_FORMAT\n> +\t}\n\nMissing comma.\n\n> +};\n> +\n> +static void print_key_value(const char *key, const char *value) {\n\nFormatting: the curly brace should sit on its own line.\n\n> +\tprintf(\"%s=\", key);\n> +\tquote_c_style(value, NULL, stdout, 0);\n> +\tputchar('\\n');\n> +}\n> +\n>  static void repo_info_init(struct repo_info *repo_info,\n>  \t\t\t   struct repository *repo,\n> -\t\t\t   char *format)\n> +\t\t\t   char *format,\n> +\t\t\t   int allow_empty,\n> +\t\t\t   int argc, const char **argv)\n>  {\n> +\tint i;\n\nThis variable can be declared in the loop itself:\n\n    for (int i = 0; ...)\n\n>  \trepo_info->repo = repo;\n>  \n>  \tif (format == NULL || !strcmp(format, \"json\"))\n> @@ -24,19 +59,83 @@ static void repo_info_init(struct repo_info *repo_info,\n>  \t\trepo_info->format = FORMAT_PLAINTEXT;\n>  \telse\n>  \t\tdie(\"invalid format %s\", format);\n> +\n> +\tif (argc == 0 && !allow_empty) {\n> +\t\trepo_info->n_fields = ARRAY_SIZE(default_fields);\n> +\t\trepo_info->fields = default_fields;\n> +\t} else {\n> +\t\trepo_info->n_fields = argc;\n> +\t\tALLOC_ARRAY(repo_info->fields, argc);\n> +\n> +\t\tfor (i = 0; i < argc; i++) {\n> +\t\t\tconst char *arg = argv[i];\n> +\t\t\tstruct repo_info_field *field = repo_info->fields + i;\n> +\n> +\t\t\tif (!strcmp(arg, \"references.format\")) {\n> +\t\t\t\tfield->category = CATEGORY_REFERENCES;\n> +\t\t\t\tfield->field.references = FIELD_REFERENCES_FORMAT;\n> +\t\t\t} else {\n> +\t\t\t\tdie(\"invalid field '%s'\", arg);\n> +\t\t\t}\n> +\t\t}\n> +\t}\n>  }\n>  \n> -static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED)\n> +static void repo_info_release(struct repo_info *repo_info)\n>  {\n> +\tif (repo_info->fields != default_fields) free(repo_info->fields);\n>  }\n>  \n> -static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n> +static void repo_info_print_plaintext(struct repo_info *repo_info) {\n\nFormatting.\n\n> +\tstruct repository *repo = repo_info->repo;\n> +\tint i;\n\nVariable can be declared in the loop.\n\n> +\tfor (i = 0; i < repo_info->n_fields; i++) {\n> +\t\tstruct repo_info_field *field = &repo_info->fields[i];\n> +\t\tswitch (field->category) {\n> +\t\tcase CATEGORY_REFERENCES:\n> +\t\t\tswitch (field->field.references) {\n\nI wonder a bit what it buys us that we have the difference between the\ncategory and reference format. Right now it feels like it can cause more\nerrors that it prevents, as we now always have doubly-nested switches.\n\nWouldn't it make more sense to only only pass around the fields as\n`repo_info_references_field`? We could then have two arrays that we\ndefine globally:\n\n    static const char const* name_by_field[] = {\n        [FIELD_REFERENCES_FORMAT] = \"references.format\",\n    };\n\n    static repo_info_category category_by_field[] = {\n        [FIELD_REFERENCES_FORMAT] = CATEGORY_REFERENCES,\n    };\n\nSo `name_by_field[FIELD_REFERENCES_FORMAT]` would yield the name and\n`category_by_field[CATEGORY_REFERENCES]` would yield its category. But\nthe benefit is that you only need to pass around the field enum from now\non, all other information is implicit.\n\nThe reverse information can also be obtained easily. To e.g. get all\nfields of a reference you'd iterate through `category_by_field` and take\nall array indices whose value matches the desired category.\n\n> +\t\t\tcase FIELD_REFERENCES_FORMAT:\n> +\t\t\t\tprint_key_value(\"references.format\",\n> +\t\t\t\t\t\tref_storage_format_to_name(\n> +\t\t\t\t\t\t\trepo->ref_storage_format));\n> +\t\t\t\tbreak;\n> +\t\t\t}\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +}\n> +\n> +static void repo_info_print_json(struct repo_info *repo_info)\n>  {\n>  \tstruct json_writer jw;\n> +\tint i;\n\nVariable can be declared in the loop.\n\n> +\tunsigned int categories = 0;\n> +\tunsigned int references_fields = 0;\n> +\tstruct repository *repo = repo_info->repo;\n> +\n> +\tfor (i = 0; i < repo_info->n_fields; i++) {\n> +\t\tstruct repo_info_field *field = repo_info->fields + i;\n> +\t\tcategories |= field->category;\n> +\t\tswitch (field->category) {\n> +\t\tcase CATEGORY_REFERENCES:\n> +\t\t\treferences_fields |= field->field.references;\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n>  \n>  \tjw_init(&jw);\n>  \n>  \tjw_object_begin(&jw, 1);\n> +\n> +\tif (categories & CATEGORY_REFERENCES) {\n> +\t\tjw_object_inline_begin_object(&jw, \"references\");\n> +\t\tif (references_fields & FIELD_REFERENCES_FORMAT) {\n> +\t\t\tconst char *format_name = ref_storage_format_to_name(\n> +\t\t\t\trepo->ref_storage_format);\n> +\t\t\tjw_object_string(&jw, \"format\", format_name);\n> +\t\t}\n> +\t\tjw_end(&jw);\n> +\t}\n>  \tjw_end(&jw);\n>  \n>  \tputs(jw.json.buf);\n\nPatrick\n"},{"id":"521262","messageId":"aGZqPC47iPMixyvW@pks.im","threadId":"63618","inReplyTo":"20250619225751.99699-7-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v2 6/7] repo-info: add field layout.bare","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-03T11:32:12Z","receivedAt":"2025-07-03T11:32:19Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jun 19, 2025 at 07:57:50PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/builtin/repo-info.c b/builtin/repo-info.c\n> index 6ce3e6134f..1650d3595c 100644\n> --- a/builtin/repo-info.c\n> +++ b/builtin/repo-info.c\n> @@ -1,4 +1,8 @@\n> +#define USE_THE_REPOSITORY_VARIABLE\n\nMeh, `is_bare_repository()` strikes again :/\n\nPatrick\n"},{"id":"521277","messageId":"6DBB30CE-D30F-42B6-A81C-35460B462D45@gmail.com","threadId":"63618","inReplyTo":"aGZqPC47iPMixyvW@pks.im","subject":"Re: [GSoC RFC PATCH v2 6/7] repo-info: add field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-03T14:14:35Z","receivedAt":"2025-07-03T14:14:50Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Meh, `is_bare_repository()` strikes again :/\n\nYeah... I tried to avoid it but removing that dependency is work for its own\npatch...\n\nI asked Ayush [1] about this as his GSoC project [2] is related to reduce the\ndependency on `the_repository`.\n\n\n[1] https://lore.kernel.org/git/CAE7as+bTKE5opov-Xn0P8R+cy+=-XRkX9Wpie_W0717XMF1b_w@mail.gmail.com/\n[2] https://summerofcode.withgoogle.com/programs/2025/projects/no7dVMeG"},{"id":"521299","messageId":"dbbde8e4-985d-4d72-b879-9110a4410abb@gmail.com","threadId":"63618","inReplyTo":"6DBB30CE-D30F-42B6-A81C-35460B462D45@gmail.com","subject":"Re: [GSoC RFC PATCH v2 6/7] repo-info: add field layout.bare","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-07-04T08:32:34Z","receivedAt":"2025-07-04T08:32:39Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Lucas\n\nOn 03/07/2025 15:14, Lucas Seiki Oshiro wrote:\n> \n>> Meh, `is_bare_repository()` strikes again :/\n> \n> Yeah... I tried to avoid it but removing that dependency is work for its own\n> patch...\n\nI really wouldn't worry too much about using \"the_repository\" in code \nthat lives under builtin/. The reason to avoid it is so that one git \nprocess can operate on multiple repositories but until our library code \nstops using \"the_repository\" and actually respects the repository passed \nto it there seems little point in worrying about using it in non-library \ncode. As it is we're currently adding more instances of \"the_repository\" \nto our library code as a stepping stone to removing other global \nvariables. Once our library code can operate on an arbitrary repository \nwe can then think about which files under builtin/ want to operate on \nmore than one repository and avoid \"the_repository\" in those. Until then \nworrying about using \"the_repository\" under builtin/ is a distraction \nfrom fixing the library code.\n\nThanks\n\nPhillip\n\n> I asked Ayush [1] about this as his GSoC project [2] is related to reduce the\n> dependency on `the_repository`.\n> \n> \n> [1] https://lore.kernel.org/git/CAE7as+bTKE5opov-Xn0P8R+cy+=-XRkX9Wpie_W0717XMF1b_w@mail.gmail.com/\n> [2] https://summerofcode.withgoogle.com/programs/2025/projects/no7dVMeG\n\n"},{"id":"521335","messageId":"6FDFCF2E-1148-4531-B957-FC61F42793F7@gmail.com","threadId":"63618","inReplyTo":"aGZqN-oqctJ79Chz@pks.im","subject":"Re: [GSoC RFC PATCH v2 5/7] repo-info: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-04T21:11:22Z","receivedAt":"2025-07-04T21:11:40Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Hm, okay. I guess later patches will add separate enums for each\n> category, so this saves us a couple bytes as the number of categories\n> grows.\n\nYeah, that's the idea.\n\nHowever, even though it saves some bytes (we don't need to keep several empty \nfields), I used a union here mostly because of what it means semantically. In\nthe print functions where I'm `switch`ing between values of one of those enums,\nthe compiler complains if I missed something :-).\n\n> I wonder a bit what it buys us that we have the difference between the\n> category and reference format. Right now it feels like it can cause more\n> errors that it prevents, as we now always have doubly-nested switches.\n\nI reckon that my solution was a little hacky bit, but I tried to solve two\nthings:\n\n1. The plaintext/linewise/null-terminated format can return the values in the\norder they were requested, like this:\n\n```\n$ git repo-info --format=plaintext layout.bare references.format layout.shallow\nlayout.bare=true\nreferences.format=files\nlayout.shallow=false\n\n```\n\n2. The same is not applicable to the JSON format, where the order shouldn't\nmatter and we can't have the same key repeating. Then it works like this:\n\n```\n$ git repo-info --format=json layout.bare references.format layout.shallow\n{\n  \"references\": {\n    \"format\": \"files\"\n  },\n  \"layout\": {\n    \"bare\": false,\n    \"shallow\": false\n  }\n}\n```\n\nbut not like this:\n\n```\n$ git repo-info --format=json layout.bare references.format layout.shallow\n{\n  \"layout\": {\n    \"bare\": false\n  },\n  \"references\": {\n    \"format\": \"files\"\n  },\n  \"layout\": {\n    \"shallow\": false\n  }\n}\n```\n.\n\nI'm still open to changes here. Perhaps keeping the same order in 1. is not so\nuseful from this v2 as I'll always return the name of the keys (like, for\nexample, git-config but unlike git-rev-parse).\n\n> Wouldn't it make more sense to only only pass around the fields as\n> `repo_info_references_field`? We could then have two arrays that we\n> define globally:\n> \n>    static const char const* name_by_field[] = {\n>        [FIELD_REFERENCES_FORMAT] = \"references.format\",\n>    };\n> \n>    static repo_info_category category_by_field[] = {\n>        [FIELD_REFERENCES_FORMAT] = CATEGORY_REFERENCES,\n>    };\n> \n> So `name_by_field[FIELD_REFERENCES_FORMAT]` would yield the name and\n> `category_by_field[CATEGORY_REFERENCES]` would yield its category. But\n> the benefit is that you only need to pass around the field enum from now\n> on, all other information is implicit.\n> \n> The reverse information can also be obtained easily. To e.g. get all\n> fields of a reference you'd iterate through `category_by_field` and take\n> all array indices whose value matches the desired category.\n\nA downside that I see is that it seems to be make the print_json function\ntoo complex. Currently, the complexities of the printing functions are (if\nI'm not missing something and ignoring the complexity of data the retrieval):\n\n- plaintext: O(n_fields), as it only iterates over the fields\n\n- json: O(n_fields + F), where F is number of possible fields that we can get,\n  as we first fill the `<category>_fields` variables and then we iterate over\n  each possible field to find if it was requested\n\nIn the plaintext format it wouldn't change too much, it would be still\nO(n_fields). But in the json format, it seems that will be something like\n(in pseudocode):\n\n```\njw = json_writer\nfields_to_print = array with F values set to false\n\nfor (field in repo_info->fields)\n    fields_to_print[field] = true\n\nfor (category in all_categories)\n    object_started = false\n\n    for (field in all_fields)\n        if (category_by_field[field] == category)\n            if (!object_started)\n                jw_object_inline_begin_object(&jw, category)\n            jw_object(&jw, name_by_field[field], retrieve(field))\n    \n    if (object_started)\n        jw_end(&jw)\n\n```\n\nwhich would be O(n_fields + n_categories * F). To be honest I'm not exactly\nthinking about performance (which would be in fact negligible in this command),\nbut how complex the code would be. If I understood it correctly, I wwould be\ntrading nested `switch`es by nested `for`s.\n\nBut again, I'm still open to change everything again here!\n\nAnyway, thank you for your time reviewing this patch, seeing what I did in the\npast weeks and joining again the discussion! :-)"},{"id":"521337","messageId":"AD2EE71A-1395-4665-BB9C-38DD4B941574@gmail.com","threadId":"63618","inReplyTo":"aGZqK5eBA18vHAa_@pks.im","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-04T21:40:11Z","receivedAt":"2025-07-04T21:40:26Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> One thing I wondered: Justin is currently iterating on git-survey(1),\n> which is the command Stolee proposed a while ago to gather repository\n> metrics.\n\nI didn't find it in the mailing list, but I remember seeing it in the\nthe GitLab's fork. Is it this unmerged MR? \nhttps://gitlab.com/gitlab-org/git/-/merge_requests/369\n\n> Would it make sense to maybe have such whole-repo commands\n> grouped together in a `git repo` top-level command? E.g. `git repo info`\n> for your command, `git repo size` to gather information about the repo\n> size.\n\nIt seems to be very nice for me! In fact, this being a home also for\nstatistics is something I considered while writing the first versions of\nmy GSoC proposal.\n\nAnd what about merging the two codes into a single API? Something like:\n\n```\ngit repo-info layout.bare references.format survey.commit-count\n{\n  \"layout\": {\n    \"bare\": true\n  },\n  \"references\": {\n    \"format\": \"files\"\n  },\n  \"survey\": {\n    \"commit-count\": 42\n  }\n}\n\n?\n\nDuring our meetings, Karthik suggested (I'm planning to it later) to also\nallow to request an entire category instead of only the fields. Then, this\nwould also be possible:\n\n```\n$ git repo-info survey\n{\n  \"survey\": {\n    \"commit-count\": 42,\n    \"blob-count\": 1234\n}\n```\n\nBut I don't know what are Justin's plans for git-survey, if it would be a\nporcelain command for showing those stats to the user of if it is targeted\nfor being parsed like this `repo-info`.\n\nI'm just brainstorming because I liked the idea :-)\n\n> Nit: missing colon. It's also present in subsequent patches.\n\nOops\n\n> Patrick\n\n"},{"id":"521397","messageId":"20250706231938.16113-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v3 0/5] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-06T23:19:33Z","receivedAt":"2025-07-06T23:20:07Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\nThis is the third version of the patchset introducing the new Git command\ncalled `repo-info`, which will return repository information currently stored\nunder git-rev-parse using machine-readable formats.\n\nThe main changes since v2 are:\n\n- Documentation for git-repo-info has been added.\n- The `--allow-empty` flag was removed. In this version, repo-info no longer\n  returns a default set of data when called without parameters. However, an\n  `--all` flag is planned for a future version.\n- The `plaintext` format was replaced by a null-terminated format, following\n  the syntax of `git config --list -z`.\n\nHere is the rangediff from v2 to v3:\n\n1:  102b5ce90a ! 1:  3f9ef00413 repo-info: declare the repo-info command\n    @@ Metadata\n      ## Commit message ##\n         repo-info: declare the repo-info command\n\n    -    Create a new Git subcommand called repo-info. `git repo-info` will query\n    -    metadata from the current repository and outputs it as JSON or plaintext.\n    +    Currently, `git rev-parse` covers a wide range of functionality not\n    +    directly related to parsing revisions, as its name says. Over time,\n    +    many features like parsing datestrings, options, paths, and others\n    +    were added to it because there wasn't a more appropriated command\n    +    to place them.\n    +\n    +    Create a new Git subcommand called `repo-info`. `git repo-info` will\n    +    bring the functionality of retrieving repository-related information\n    +    currently returned by `rev-parse`, displaying them in\n    +    machine-readable formats (JSON or a null-terminated format).\n\n         Also add entries for this new command in:\n\n    -    - the build files (Makefile and meson.build)\n    +    - the build files (Makefile, meson.build, Documentation/meson.build)\n         - builtin.h\n         - git.c\n         - .gitignore\n    +    - command-list.txt\n    +    - Documentation\n\n    -    In option parsing, use PARSE_OPT_KEEP_UNKNOWN_OPT to allow the users\n    -    specify after the flags the information that they want to retrieve.\n    -\n    +    Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n    +    Helped-by: Junio C Hamano <gitster@pobox.com>\n         Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n    -    Mentored-by Patrick Steinhardt <ps@pks.im>\n    +    Mentored-by: Patrick Steinhardt <ps@pks.im>\n         Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n\n      ## .gitignore ##\n    @@ .gitignore\n      /git-rerere\n      /git-reset\n\n    + ## Documentation/git-repo-info.adoc (new) ##\n    +@@\n    ++git-repo-info(1)\n    ++================\n    ++\n    ++NAME\n    ++----\n    ++git-repo-info - Retrieve information about a repository\n    ++\n    ++SYNOPSIS\n    ++--------\n    ++[synopsis]\n    ++git repo-info\n    ++\n    ++DESCRIPTION\n    ++-----------\n    ++Retrieve information about the current repository in a machine-readable format.\n    ++\n    ++`git repo-info` will be the primary tool to query repository-specific\n    ++information, which currently can also be done by calling `git rev-parse` (see\n    ++linkgit:git-rev-parse[1]). `git repo-info` doesn't query information unrelated\n    ++to the current repository or that is already retrieved by a specialized command,\n    ++for example, `git config` (see linkgit:git-config[1]) or `git var` (see\n    ++linkgit:git-var[1]).\n    ++\n    ++THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n    ++\n    ++OPTIONS\n    ++-------\n    ++\n    ++`<key>`::\n    ++Key of the value that will be retrieved. It should be in the format\n    ++`<category>.<field>`. See the \"CATEGORIES AND FIELDS\" section below.\n    ++\n    ++CATEGORIES AND FIELDS\n    ++---------------------\n    ++\n    ++The set of data that `git repo-info` can return is divided into\n    ++categories. Each category is composed by one or more fields.\n    ++\n    ++SEE ALSO\n    ++--------\n    ++linkgit:git-rev-parse[1]\n    ++\n    ++GIT\n    ++---\n    ++Part of the linkgit:git[1] suite\n    +\n    + ## Documentation/meson.build ##\n    +@@ Documentation/meson.build: manpages = {\n    +   'git-repack.adoc' : 1,\n    +   'git-replace.adoc' : 1,\n    +   'git-replay.adoc' : 1,\n    ++  'git-repo-info.adoc' : 1,\n    +   'git-request-pull.adoc' : 1,\n    +   'git-rerere.adoc' : 1,\n    +   'git-reset.adoc' : 1,\n    +\n      ## Makefile ##\n     @@ Makefile: BUILTIN_OBJS += builtin/remote.o\n      BUILTIN_OBJS += builtin/repack.o\n    @@ builtin/repo-info.c (new)\n     +\t};\n     +\n     +\targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n    -+\t\t\t     PARSE_OPT_KEEP_UNKNOWN_OPT);\n    ++\t\t\t     0);\n     +\n     +\treturn 0;\n     +}\n\n    + ## command-list.txt ##\n    +@@ command-list.txt: git-remote                              ancillarymanipulators           complete\n    + git-repack                              ancillarymanipulators           complete\n    + git-replace                             ancillarymanipulators           complete\n    + git-replay                              plumbingmanipulators\n    ++git-repo-info                           plumbinginterrogators\n    + git-request-pull                        foreignscminterface             complete\n    + git-rerere                              ancillaryinterrogators\n    + git-reset                               mainporcelain           history\n    +\n      ## git.c ##\n     @@ git.c: static struct cmd_struct commands[] = {\n      \t{ \"repack\", cmd_repack, RUN_SETUP },\n2:  1cc1184663 < -:  ---------- repo-info: add the --format flag\n3:  a329825387 < -:  ---------- repo-info: add plaintext as an output format\n4:  3b6c27b68d < -:  ---------- repo-info: add the --allow-empty flag\n5:  4b1e99cac4 < -:  ---------- repo-info: add the field references.format\n6:  0cd72e5ebf < -:  ---------- repo-info: add field layout.bare\n7:  274dfbde86 < -:  ---------- repo-info: add field layout.shallow\n-:  ---------- > 2:  33c3d5e665 repo-info: add the --format flag\n-:  ---------- > 3:  e6331b18d4 repo-info: add the field references.format\n-:  ---------- > 4:  6a6825c4df repo-info: add field layout.bare\n-:  ---------- > 5:  938be682d2 repo-info: add field layout.shallow\n\nLucas Seiki Oshiro (5):\n  repo-info: declare the repo-info command\n  repo-info: add the --format flag\n  repo-info: add the field references.format\n  repo-info: add field layout.bare\n  repo-info: add field layout.shallow\n\n .gitignore                       |   1 +\n Documentation/git-repo-info.adoc |  84 ++++++++++++\n Documentation/meson.build        |   1 +\n Makefile                         |   1 +\n builtin.h                        |   1 +\n builtin/repo-info.c              | 229 +++++++++++++++++++++++++++++++\n command-list.txt                 |   1 +\n git.c                            |   1 +\n meson.build                      |   1 +\n t/meson.build                    |   1 +\n t/t1900-repo-info.sh             |  84 ++++++++++++\n 11 files changed, 405 insertions(+)\n create mode 100644 Documentation/git-repo-info.adoc\n create mode 100644 builtin/repo-info.c\n create mode 100755 t/t1900-repo-info.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521398","messageId":"20250706231938.16113-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250706231938.16113-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v3 1/5] repo-info: declare the repo-info command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-06T23:19:34Z","receivedAt":"2025-07-06T23:20:10Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, `git rev-parse` covers a wide range of functionality not\ndirectly related to parsing revisions, as its name says. Over time,\nmany features like parsing datestrings, options, paths, and others\nwere added to it because there wasn't a more appropriated command\nto place them.\n\nCreate a new Git subcommand called `repo-info`. `git repo-info` will\nbring the functionality of retrieving repository-related information\ncurrently returned by `rev-parse`, displaying them in\nmachine-readable formats (JSON or a null-terminated format).\n\nAlso add entries for this new command in:\n\n- the build files (Makefile, meson.build, Documentation/meson.build)\n- builtin.h\n- git.c\n- .gitignore\n- command-list.txt\n- Documentation\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore                       |  1 +\n Documentation/git-repo-info.adoc | 45 ++++++++++++++++++++++++++++++++\n Documentation/meson.build        |  1 +\n Makefile                         |  1 +\n builtin.h                        |  1 +\n builtin/repo-info.c              | 21 +++++++++++++++\n command-list.txt                 |  1 +\n git.c                            |  1 +\n meson.build                      |  1 +\n 9 files changed, 73 insertions(+)\n create mode 100644 Documentation/git-repo-info.adoc\n create mode 100644 builtin/repo-info.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..b2f3fb0047 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo-info\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Documentation/git-repo-info.adoc b/Documentation/git-repo-info.adoc\nnew file mode 100644\nindex 0000000000..d5f34fc46e\n--- /dev/null\n+++ b/Documentation/git-repo-info.adoc\n@@ -0,0 +1,45 @@\n+git-repo-info(1)\n+================\n+\n+NAME\n+----\n+git-repo-info - Retrieve information about a repository\n+\n+SYNOPSIS\n+--------\n+[synopsis]\n+git repo-info\n+\n+DESCRIPTION\n+-----------\n+Retrieve information about the current repository in a machine-readable format.\n+\n+`git repo-info` will be the primary tool to query repository-specific\n+information, which currently can also be done by calling `git rev-parse` (see\n+linkgit:git-rev-parse[1]). `git repo-info` doesn't query information unrelated\n+to the current repository or that is already retrieved by a specialized command,\n+for example, `git config` (see linkgit:git-config[1]) or `git var` (see\n+linkgit:git-var[1]).\n+\n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n+OPTIONS\n+-------\n+\n+`<key>`::\n+Key of the value that will be retrieved. It should be in the format\n+`<category>.<field>`. See the \"CATEGORIES AND FIELDS\" section below.\n+\n+CATEGORIES AND FIELDS\n+---------------------\n+\n+The set of data that `git repo-info` can return is divided into\n+categories. Each category is composed by one or more fields.\n+\n+SEE ALSO\n+--------\n+linkgit:git-rev-parse[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 2fe1a1369d..1369523741 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -116,6 +116,7 @@ manpages = {\n   'git-repack.adoc' : 1,\n   'git-replace.adoc' : 1,\n   'git-replay.adoc' : 1,\n+  'git-repo-info.adoc' : 1,\n   'git-request-pull.adoc' : 1,\n   'git-rerere.adoc' : 1,\n   'git-reset.adoc' : 1,\ndiff --git a/Makefile b/Makefile\nindex 70d1543b6b..50e3a3cbcc 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1308,6 +1308,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo-info.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..cc6bc95962 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo_info(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nnew file mode 100644\nindex 0000000000..0180c89908\n--- /dev/null\n+++ b/builtin/repo-info.c\n@@ -0,0 +1,21 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+int cmd_repo_info(int argc,\n+\t\t  const char **argv,\n+\t\t  const char *prefix,\n+\t\t  struct repository *repo UNUSED)\n+{\n+\tconst char *const repo_info_usage[] = {\n+\t\t\"git repo-info\",\n+\t\tNULL\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n+\t\t\t     0);\n+\n+\treturn 0;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex b7ade3ab9f..197ac21706 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -164,6 +164,7 @@ git-remote                              ancillarymanipulators           complete\n git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-replay                              plumbingmanipulators\n+git-repo-info                           plumbinginterrogators\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           history\ndiff --git a/git.c b/git.c\nindex 07a5fe39fb..27a2b3569b 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo-info\", cmd_repo_info, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 7fea4a34d6..06f2f647ba 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo-info.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521399","messageId":"20250706231938.16113-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250706231938.16113-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v3 2/5] repo-info: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-06T23:19:35Z","receivedAt":"2025-07-06T23:20:13Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, git-rev-parse with the 'options for files' flags returns only\nthe requested values (e.g. `git rev-parse --show-toplevel\n--show-ref-format` returns the top-level repository and the reference\nstorage format), dumping them only separated by line feeds.\n\nHowever, from the perspective of a programmatic interface, this format\nis not a good choice as, for example:\n\n- it doesn't show the keys of the requested fields, meaning that the\n  caller depends on the order of the requested fields for parsing them;\n\n- there's no guarantee that each line contains the value of one field,\n  for example, the returned values may contain line feeds.\n\nThis way, given that git-repo-info aims to produce a machine-readable\noutput, it needs to follow other formats that are better suited for\nthose means, for example:\n\n- JSON, which is widely used as a data exchange format;\n\n- null-terminated, composed zero or more <key><LF><value><NUL> entries.\n\nAdd the --format flag to the repo-info command, allowing the user to\nchoose between output formats. Add as options \"json\" and\n\"null-terminated\", and use \"json\" as the default format.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo-info.adoc | 32 ++++++++++++++++-\n builtin/repo-info.c              | 61 ++++++++++++++++++++++++++++++--\n 2 files changed, 90 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-repo-info.adoc b/Documentation/git-repo-info.adoc\nindex d5f34fc46e..bf1d391482 100644\n--- a/Documentation/git-repo-info.adoc\n+++ b/Documentation/git-repo-info.adoc\n@@ -8,7 +8,7 @@ git-repo-info - Retrieve information about a repository\n SYNOPSIS\n --------\n [synopsis]\n-git repo-info\n+git repo-info [--format <format>] [<field>...]\n \n DESCRIPTION\n -----------\n@@ -25,6 +25,36 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n \n OPTIONS\n -------\n+`--format`::\n+Specify the output format `<format>`. The valid values are:\n++\n+* `json`: output the data in JSON format, following this schema:\n++\n+----------------\n+{\n+  \"category1\": {\n+    \"field1\": value1,\n+    \"field2\": value2,\n+    ...\n+  },\n+  \"category2\": {\n+    \"field3\": value3,\n+    ...\n+  },\n+  ...\n+}\n+----------------\n+* `null-terminated`: output the data in a null-terminated format,\n+following this syntax:\n++\n+----------------\n+category1.field1<LF>value1<NUL>\n+category1.field2<LF>value2<NUL>\n+...\n+----------------\n++\n+In this format, the fields will be returned in the same order they were\n+requested.\n \n `<key>`::\n Key of the value that will be retrieved. It should be in the format\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex 0180c89908..cb4785169f 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -1,21 +1,78 @@\n #include \"builtin.h\"\n+#include \"json-writer.h\"\n #include \"parse-options.h\"\n \n+enum output_format {\n+\tFORMAT_JSON,\n+\tFORMAT_NULL_TERMINATED,\n+};\n+\n+struct repo_info {\n+\tstruct repository *repo;\n+\tenum output_format format;\n+};\n+\n+static void repo_info_init(struct repo_info *repo_info,\n+\t\t\t   struct repository *repo,\n+\t\t\t   const char *format)\n+{\n+\trepo_info->repo = repo;\n+\n+\tif (!format || !strcmp(format, \"json\"))\n+\t\trepo_info->format = FORMAT_JSON;\n+\telse if (!strcmp(format, \"null-terminated\"))\n+\t\trepo_info->format = FORMAT_NULL_TERMINATED;\n+\telse\n+\t\tdie(\"invalid format %s\", format);\n+}\n+\n+static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n+{\n+\tstruct json_writer jw;\n+\n+\tjw_init(&jw);\n+\n+\tjw_object_begin(&jw, 1);\n+\tjw_end(&jw);\n+\n+\tputs(jw.json.buf);\n+\tjw_release(&jw);\n+}\n+\n+static void repo_info_print(struct repo_info *repo_info)\n+{\n+\tswitch (repo_info->format) {\n+\tcase FORMAT_JSON:\n+\t\trepo_info_print_json(repo_info);\n+\t\tbreak;\n+\tcase FORMAT_NULL_TERMINATED:\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"%d: not a valid repo-info format\", repo_info->format);\n+\t}\n+}\n+\n int cmd_repo_info(int argc,\n \t\t  const char **argv,\n \t\t  const char *prefix,\n-\t\t  struct repository *repo UNUSED)\n+\t\t  struct repository *repo)\n {\n \tconst char *const repo_info_usage[] = {\n-\t\t\"git repo-info\",\n+\t\t\"git repo-info [--format <format>] [<field>...]\",\n \t\tNULL\n \t};\n+\tstruct repo_info repo_info;\n+\tconst char *format = NULL;\n \tstruct option options[] = {\n+\t\tOPT_STRING(0, \"format\", &format, N_(\"format\"),\n+\t\t\t   N_(\"output format\")),\n \t\tOPT_END()\n \t};\n \n \targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n \t\t\t     0);\n+\trepo_info_init(&repo_info, repo, format);\n+\trepo_info_print(&repo_info);\n \n \treturn 0;\n }\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521400","messageId":"20250706231938.16113-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250706231938.16113-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v3 3/5] repo-info: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-06T23:19:36Z","receivedAt":"2025-07-06T23:20:15Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduce the new command\ngit-repo-info.\n\nThe flag `--show-ref-format` from git-rev-parse is used for retrieving\nthe reference format (i.e. `files` or `reftable`). This way, it is\nused for querying repository information, fitting in the purpose of\ngit-repo-info.\n\nThen, add a new field `references.format` to the repo-info command\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo-info.adoc |   4 ++\n builtin/repo-info.c              | 105 ++++++++++++++++++++++++++++++-\n t/meson.build                    |   1 +\n t/t1900-repo-info.sh             |  64 +++++++++++++++++++\n 4 files changed, 171 insertions(+), 3 deletions(-)\n create mode 100755 t/t1900-repo-info.sh\n\ndiff --git a/Documentation/git-repo-info.adoc b/Documentation/git-repo-info.adoc\nindex bf1d391482..dd221b236e 100644\n--- a/Documentation/git-repo-info.adoc\n+++ b/Documentation/git-repo-info.adoc\n@@ -66,6 +66,10 @@ CATEGORIES AND FIELDS\n The set of data that `git repo-info` can return is divided into\n categories. Each category is composed by one or more fields.\n \n+`references`::\n+Reference-related data:\n+* `format`: the reference storage format, either `files` or `reftable`.\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex cb4785169f..98a0d83d51 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -1,20 +1,39 @@\n #include \"builtin.h\"\n #include \"json-writer.h\"\n #include \"parse-options.h\"\n+#include \"refs.h\"\n \n enum output_format {\n \tFORMAT_JSON,\n \tFORMAT_NULL_TERMINATED,\n };\n \n+enum repo_info_category {\n+\tCATEGORY_REFERENCES = 1 << 0,\n+};\n+\n+enum repo_info_references_field {\n+\tFIELD_REFERENCES_FORMAT = 1 << 0,\n+};\n+\n+struct repo_info_field {\n+\tenum repo_info_category category;\n+\tunion {\n+\t\tenum repo_info_references_field references;\n+\t} u;\n+};\n+\n struct repo_info {\n \tstruct repository *repo;\n \tenum output_format format;\n+\tsize_t fields_nr;\n+\tstruct repo_info_field *fields;\n };\n \n static void repo_info_init(struct repo_info *repo_info,\n \t\t\t   struct repository *repo,\n-\t\t\t   const char *format)\n+\t\t\t   const char *format,\n+\t\t\t   int argc, const char **argv)\n {\n \trepo_info->repo = repo;\n \n@@ -24,15 +43,93 @@ static void repo_info_init(struct repo_info *repo_info,\n \t\trepo_info->format = FORMAT_NULL_TERMINATED;\n \telse\n \t\tdie(\"invalid format %s\", format);\n+\n+\trepo_info->fields_nr = argc;\n+\tALLOC_ARRAY(repo_info->fields, argc);\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\t\tstruct repo_info_field *field = repo_info->fields + i;\n+\t\tif (!strcmp(arg, \"references.format\")) {\n+\t\t\tfield->category = CATEGORY_REFERENCES;\n+\t\t\tfield->u.references = FIELD_REFERENCES_FORMAT;\n+\t\t} else {\n+\t\t\tdie(\"invalid field '%s'\", arg);\n+\t\t}\n+\t}\n+}\n+\n+static void repo_info_release(struct repo_info *repo_info)\n+{\n+\tfree(repo_info->fields);\n }\n \n-static void repo_info_print_json(struct repo_info *repo_info UNUSED)\n+static void append_null_terminated_field(struct strbuf *buf,\n+\t\t\t\t\t struct repo_info *repo_info,\n+\t\t\t\t\t struct repo_info_field *field)\n+{\n+\tstruct repository *repo = repo_info->repo;\n+\n+\tswitch (field->category) {\n+\tcase CATEGORY_REFERENCES:\n+\t\tstrbuf_addstr(buf, \"references.\");\n+\t\tswitch (field->u.references) {\n+\t\tcase FIELD_REFERENCES_FORMAT:\n+\t\t\tstrbuf_addstr(buf, \"format\\n\");\n+\t\t\tstrbuf_addstr(buf, ref_storage_format_to_name(\n+\t\t\t\t\t\t   repo->ref_storage_format));\n+\t\t\tbreak;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\n+\tstrbuf_addch(buf, '\\0');\n+}\n+\n+static void repo_info_print_null_terminated(struct repo_info *repo_info)\n+{\n+\tstruct strbuf buf;\n+\n+\tstrbuf_init(&buf, 256);\n+\n+\tfor (size_t i = 0; i < repo_info->fields_nr; i++) {\n+\t\tstruct repo_info_field *field = &repo_info->fields[i];\n+\t\tappend_null_terminated_field(&buf, repo_info, field);\n+\t}\n+\n+\tfwrite(buf.buf, 1, buf.len, stdout);\n+\tstrbuf_release(&buf);\n+}\n+\n+static void repo_info_print_json(struct repo_info *repo_info)\n {\n \tstruct json_writer jw;\n+\tunsigned int categories = 0;\n+\tunsigned int references_fields = 0;\n+\tstruct repository *repo = repo_info->repo;\n+\n+\tfor (size_t i = 0; i < repo_info->fields_nr; i++) {\n+\t\tstruct repo_info_field *field = repo_info->fields + i;\n+\t\tcategories |= field->category;\n+\t\tswitch (field->category) {\n+\t\tcase CATEGORY_REFERENCES:\n+\t\t\treferences_fields |= field->u.references;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n \n \tjw_init(&jw);\n \n \tjw_object_begin(&jw, 1);\n+\tif (categories & CATEGORY_REFERENCES) {\n+\t\tjw_object_inline_begin_object(&jw, \"references\");\n+\t\tif (references_fields & FIELD_REFERENCES_FORMAT) {\n+\t\t\tconst char *format_name = ref_storage_format_to_name(\n+\t\t\t\trepo->ref_storage_format);\n+\t\t\tjw_object_string(&jw, \"format\", format_name);\n+\t\t}\n+\t\tjw_end(&jw);\n+\t}\n \tjw_end(&jw);\n \n \tputs(jw.json.buf);\n@@ -46,6 +143,7 @@ static void repo_info_print(struct repo_info *repo_info)\n \t\trepo_info_print_json(repo_info);\n \t\tbreak;\n \tcase FORMAT_NULL_TERMINATED:\n+\t\trepo_info_print_null_terminated(repo_info);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"%d: not a valid repo-info format\", repo_info->format);\n@@ -71,8 +169,9 @@ int cmd_repo_info(int argc,\n \n \targc = parse_options(argc, argv, prefix, options, repo_info_usage,\n \t\t\t     0);\n-\trepo_info_init(&repo_info, repo, format);\n+\trepo_info_init(&repo_info, repo, format, argc, argv);\n \trepo_info_print(&repo_info);\n+\trepo_info_release(&repo_info);\n \n \treturn 0;\n }\ndiff --git a/t/meson.build b/t/meson.build\nindex 6d7fe6b117..e2c9393189 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -245,6 +245,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo-info.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nnew file mode 100755\nindex 0000000000..2af9d1d9c3\n--- /dev/null\n+++ b/t/t1900-repo-info.sh\n@@ -0,0 +1,64 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+\n+. ./test-lib.sh\n+\n+parse_json () {\n+\ttr '\\n' ' ' | \"$PERL_PATH\" \"$TEST_DIRECTORY/t0019/parse_json.perl\"\n+}\n+\n+test_lazy_prereq PERLJSON '\n+\tperl -MJSON -e \"exit 0\"\n+'\n+\n+# Test if a field is correctly returned in both null-terminated and json formats.\n+#\n+# Usage: test_repo_info <label> <init command> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init command: a command that creates a repository called 'repo', configured\n+#      accordingly to what is being tested\n+#   key: the key of the field that is being tested\n+#   expected value: the value that the field should contain\n+test_repo_info () {\n+        label=$1\n+        init_command=$2\n+        key=$3\n+        expected_value=$4\n+\n+        test_expect_success PERLJSON \"json: $label\" '\n+\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\teval \"$init_command\" &&\n+\t\techo \"$expected_value\" >expect &&\n+\t\tgit -C repo repo-info \"$key\" >output &&\n+\t\tparse_json <output >parsed &&\n+\t\tgrep -F \"row[0].$key\" parsed | cut -d \" \" -f 2 >value &&\n+\t\tsed -n -e \"/row[0].$key/{\n+\t\t\ts/^[^ ]* //\n+\t\t\ts/^1\\$/true/\n+\t\t\ts/^0\\$/false/\n+\t\t\tp;\n+\t\t\t}\" parsed >actual &&\n+\t\tsed \"s/^0$/false/\" <value| sed \"s/^1$/true/\" >actual &&\n+\t\ttest_cmp expect actual\n+        '\n+\n+        test_expect_success \"null-terminated: $label\" '\n+\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\teval \"$init_command\" &&\n+\t\techo \"$expected_value\" | lf_to_nul >expect &&\n+\t\tgit -C repo repo-info --format=null-terminated \"$key\" >output &&\n+\t\ttail -n 1 output >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_repo_info 'ref format files is retrieved correctly' '\n+\tgit init --ref-format=files repo' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' '\n+\tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521401","messageId":"20250706231938.16113-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250706231938.16113-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v3 4/5] repo-info: add field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-06T23:19:37Z","receivedAt":"2025-07-06T23:20:18Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new command\ngit-repo-info.\n\nThe flag --is-bare-repository from git-rev-parse is used for retrieving\nwhether the current repository is bare . This way, it is used for\nquerying repository information, fitting in the purpose of\ngit-repo-info.\n\nThen, add a new field layout.bare to the git-repo-info command\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo-info.adoc |  4 ++++\n builtin/repo-info.c              | 37 ++++++++++++++++++++++++++++++++\n t/t1900-repo-info.sh             |  6 ++++++\n 3 files changed, 47 insertions(+)\n\ndiff --git a/Documentation/git-repo-info.adoc b/Documentation/git-repo-info.adoc\nindex dd221b236e..67d19406ad 100644\n--- a/Documentation/git-repo-info.adoc\n+++ b/Documentation/git-repo-info.adoc\n@@ -70,6 +70,10 @@ categories. Each category is composed by one or more fields.\n Reference-related data:\n * `format`: the reference storage format, either `files` or `reftable`.\n \n+`layout`::\n+Information about the how the current repository is represented:\n+* `bare`: `true` if this is a bare repository, otherwise `false`.\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex 98a0d83d51..7e29ae8519 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -1,4 +1,8 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n+#include \"environment.h\"\n+#include \"hash.h\"\n #include \"json-writer.h\"\n #include \"parse-options.h\"\n #include \"refs.h\"\n@@ -10,16 +14,22 @@ enum output_format {\n \n enum repo_info_category {\n \tCATEGORY_REFERENCES = 1 << 0,\n+\tCATEGORY_LAYOUT = 1 << 1,\n };\n \n enum repo_info_references_field {\n \tFIELD_REFERENCES_FORMAT = 1 << 0,\n };\n \n+enum repo_info_layout_field {\n+\tFIELD_LAYOUT_BARE = 1 << 0,\n+};\n+\n struct repo_info_field {\n \tenum repo_info_category category;\n \tunion {\n \t\tenum repo_info_references_field references;\n+\t\tenum repo_info_layout_field layout;\n \t} u;\n };\n \n@@ -53,6 +63,9 @@ static void repo_info_init(struct repo_info *repo_info,\n \t\tif (!strcmp(arg, \"references.format\")) {\n \t\t\tfield->category = CATEGORY_REFERENCES;\n \t\t\tfield->u.references = FIELD_REFERENCES_FORMAT;\n+\t\t} else if (!strcmp(arg, \"layout.bare\")) {\n+\t\t\tfield->category = CATEGORY_LAYOUT;\n+\t\t\tfield->u.layout = FIELD_LAYOUT_BARE;\n \t\t} else {\n \t\t\tdie(\"invalid field '%s'\", arg);\n \t\t}\n@@ -81,6 +94,17 @@ static void append_null_terminated_field(struct strbuf *buf,\n \t\t\tbreak;\n \t\t}\n \t\tbreak;\n+\n+\tcase CATEGORY_LAYOUT:\n+\t\tstrbuf_addstr(buf, \"layout.\");\n+\t\tswitch (field->u.layout) {\n+\t\tcase FIELD_LAYOUT_BARE:\n+\t\t\tstrbuf_addstr(buf, \"bare\\n\");\n+\t\t\tstrbuf_addstr(buf, is_bare_repository() ? \"true\" :\n+\t\t\t\t\t\t\t\t  \"false\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tbreak;\n \t}\n \n \tstrbuf_addch(buf, '\\0');\n@@ -106,6 +130,7 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \tstruct json_writer jw;\n \tunsigned int categories = 0;\n \tunsigned int references_fields = 0;\n+\tunsigned int layout_fields = 0;\n \tstruct repository *repo = repo_info->repo;\n \n \tfor (size_t i = 0; i < repo_info->fields_nr; i++) {\n@@ -115,6 +140,9 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \t\tcase CATEGORY_REFERENCES:\n \t\t\treferences_fields |= field->u.references;\n \t\t\tbreak;\n+\t\tcase CATEGORY_LAYOUT:\n+\t\t\tlayout_fields |= field->u.layout;\n+\t\t\tbreak;\n \t\t}\n \t}\n \n@@ -130,6 +158,15 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \t\t}\n \t\tjw_end(&jw);\n \t}\n+\n+\tif (categories & CATEGORY_LAYOUT) {\n+\t\tjw_object_inline_begin_object(&jw, \"layout\");\n+\t\tif (layout_fields & FIELD_LAYOUT_BARE) {\n+\t\t\tjw_object_bool(&jw, \"bare\",\n+\t\t\t\t       is_bare_repository());\n+\t\t}\n+\t\tjw_end(&jw);\n+\t}\n \tjw_end(&jw);\n \n \tputs(jw.json.buf);\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 2af9d1d9c3..246c4bc40c 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -61,4 +61,10 @@ test_repo_info 'ref format files is retrieved correctly' '\n test_repo_info 'ref format reftable is retrieved correctly' '\n \tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' '\n+\tgit init --bare repo' 'layout.bare' 'true'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521402","messageId":"20250706231938.16113-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250706231938.16113-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v3 5/5] repo-info: add field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-06T23:19:38Z","receivedAt":"2025-07-06T23:20:21Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new command\ngit-repo-info.\n\nThe flag `--is-shallow-repository` from git-rev-parse is used for\nretrieving whether the repository is shallow. This way, it is used for\nquerying repository information, fitting in the purpose of\ngit-repo-info.\n\nThen, add a new field `layout.shallow` to the git-repo-info command\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo-info.adoc |  1 +\n builtin/repo-info.c              | 15 +++++++++++++++\n t/t1900-repo-info.sh             | 14 ++++++++++++++\n 3 files changed, 30 insertions(+)\n\ndiff --git a/Documentation/git-repo-info.adoc b/Documentation/git-repo-info.adoc\nindex 67d19406ad..3261cd97b5 100644\n--- a/Documentation/git-repo-info.adoc\n+++ b/Documentation/git-repo-info.adoc\n@@ -73,6 +73,7 @@ Reference-related data:\n `layout`::\n Information about the how the current repository is represented:\n * `bare`: `true` if this is a bare repository, otherwise `false`.\n+* `shallow`: `true` if this is a shallow repository, otherwise `false`.\n \n SEE ALSO\n --------\ndiff --git a/builtin/repo-info.c b/builtin/repo-info.c\nindex 7e29ae8519..2fa6544d15 100644\n--- a/builtin/repo-info.c\n+++ b/builtin/repo-info.c\n@@ -6,6 +6,7 @@\n #include \"json-writer.h\"\n #include \"parse-options.h\"\n #include \"refs.h\"\n+#include \"shallow.h\"\n \n enum output_format {\n \tFORMAT_JSON,\n@@ -23,6 +24,7 @@ enum repo_info_references_field {\n \n enum repo_info_layout_field {\n \tFIELD_LAYOUT_BARE = 1 << 0,\n+\tFIELD_LAYOUT_SHALLOW = 1 << 1,\n };\n \n struct repo_info_field {\n@@ -66,6 +68,9 @@ static void repo_info_init(struct repo_info *repo_info,\n \t\t} else if (!strcmp(arg, \"layout.bare\")) {\n \t\t\tfield->category = CATEGORY_LAYOUT;\n \t\t\tfield->u.layout = FIELD_LAYOUT_BARE;\n+\t\t} else if (!strcmp(arg, \"layout.shallow\")) {\n+\t\t\tfield->category = CATEGORY_LAYOUT;\n+\t\t\tfield->u.layout = FIELD_LAYOUT_SHALLOW;\n \t\t} else {\n \t\t\tdie(\"invalid field '%s'\", arg);\n \t\t}\n@@ -103,6 +108,11 @@ static void append_null_terminated_field(struct strbuf *buf,\n \t\t\tstrbuf_addstr(buf, is_bare_repository() ? \"true\" :\n \t\t\t\t\t\t\t\t  \"false\");\n \t\t\tbreak;\n+\t\tcase FIELD_LAYOUT_SHALLOW:\n+\t\t\tstrbuf_addstr(buf, \"shallow\\n\");\n+\t\t\tstrbuf_addstr(buf, is_repository_shallow(repo) ? \"true\" :\n+\t\t\t\t\t\t\t\t\t \"false\");\n+\t\t\tbreak;\n \t\t}\n \t\tbreak;\n \t}\n@@ -165,6 +175,11 @@ static void repo_info_print_json(struct repo_info *repo_info)\n \t\t\tjw_object_bool(&jw, \"bare\",\n \t\t\t\t       is_bare_repository());\n \t\t}\n+\n+\t\tif (layout_fields & FIELD_LAYOUT_SHALLOW) {\n+\t\t\tjw_object_bool(&jw, \"shallow\",\n+\t\t\t\t       is_repository_shallow(repo));\n+\t\t}\n \t\tjw_end(&jw);\n \t}\n \tjw_end(&jw);\ndiff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh\nindex 246c4bc40c..fdbbfb42a0 100755\n--- a/t/t1900-repo-info.sh\n+++ b/t/t1900-repo-info.sh\n@@ -67,4 +67,18 @@ test_repo_info 'bare repository = false is retrieved correctly' '\n test_repo_info 'bare repository = true is retrieved correctly' '\n \tgit init --bare repo' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.shallow' 'false'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' '\n+\tgit init remote &&\n+\tcd remote &&\n+\techo x >x &&\n+\tgit add x &&\n+\tgit commit -m x &&\n+\tcd .. &&\n+\tgit clone --depth 1 \"file://$PWD/remote\" repo &&\n+\trm -rf remote\n+\t' 'layout.shallow' 'true'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521411","messageId":"aGtiyGW4MIYUw3Ed@pks.im","threadId":"63618","inReplyTo":"AD2EE71A-1395-4665-BB9C-38DD4B941574@gmail.com","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-07T06:01:51Z","receivedAt":"2025-07-07T06:02:05Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Jul 04, 2025 at 06:40:11PM -0300, Lucas Seiki Oshiro wrote:\n> \n> > One thing I wondered: Justin is currently iterating on git-survey(1),\n> > which is the command Stolee proposed a while ago to gather repository\n> > metrics.\n> \n> I didn't find it in the mailing list, but I remember seeing it in the\n> the GitLab's fork. Is it this unmerged MR? \n> https://gitlab.com/gitlab-org/git/-/merge_requests/369\n\nYup. It's been posted to the mailing list as part of [1].\n\n> > Would it make sense to maybe have such whole-repo commands\n> > grouped together in a `git repo` top-level command? E.g. `git repo info`\n> > for your command, `git repo size` to gather information about the repo\n> > size.\n> \n> It seems to be very nice for me! In fact, this being a home also for\n> statistics is something I considered while writing the first versions of\n> my GSoC proposal.\n> \n> And what about merging the two codes into a single API? Something like:\n> \n> ```\n> git repo-info layout.bare references.format survey.commit-count\n> {\n>   \"layout\": {\n>     \"bare\": true\n>   },\n>   \"references\": {\n>     \"format\": \"files\"\n>   },\n>   \"survey\": {\n>     \"commit-count\": 42\n>   }\n> }\n> \n> ?\n\nWe could in theory do that. But there's two things we need to be\ncautious about:\n\n  1. We should be mindful about what specifically this tool is about. It\n     shouldn't become the next tool that does way too many different\n     things.\n\n  2. One of the idea of git-survey(1) is to eventually replace\n     git-sizer(1). This will require very specific presentation formats\n     that aren't really compatible with any of the other information.\n\nOut of these two I think the second item is the more important one why\ngit-survey(1) should exist as a standalone tool, either as a top-level\ncommand or as a subcommand.\n\n> During our meetings, Karthik suggested (I'm planning to it later) to also\n> allow to request an entire category instead of only the fields. Then, this\n> would also be possible:\n> \n> ```\n> $ git repo-info survey\n> {\n>   \"survey\": {\n>     \"commit-count\": 42,\n>     \"blob-count\": 1234\n> }\n> ```\n\nIt raises another question though: if we ever were to add `--all` we'll\nneed to step a bit careful about what kind of information we add to this\ntool. All of the information proposed so far can be computed rather\ntrivially. But computing repository sizes has way higher computational\ncomplexity and may easily take seconds, maybe even minutes in large\nrepositories.\n\nThat to me further points into the direction of giving those two tools a\ncommon top-level command (`git repo info`, `git repo survey`), but to\nnot mix concerns too much with one another.\n\n> But I don't know what are Justin's plans for git-survey, if it would be a\n> porcelain command for showing those stats to the user of if it is targeted\n> for being parsed like this `repo-info`.\n> \n> I'm just brainstorming because I liked the idea :-)\n\nI've already pinged him on this and he liked the idea of having a common\ntop-level command. I guess he'll respond later today or this week.\n\nPatrick\n"},{"id":"521523","messageId":"c1f871ec-96a0-4dbc-b84b-4add36bec682@gmail.com","threadId":"63618","inReplyTo":"20250706231938.16113-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v3 0/5] repo-info: add new command for retrieving repository info","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-07-08T10:11:58Z","receivedAt":"2025-07-08T10:12:01Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Lucas\n\nOn 07/07/2025 00:19, Lucas Seiki Oshiro wrote:\n> Hi!\n> \n> This is the third version of the patchset introducing the new Git command\n> called `repo-info`, which will return repository information currently stored\n> under git-rev-parse using machine-readable formats.\n> The main changes since v2 are:\n> \n> - Documentation for git-repo-info has been added.\n> - The `--allow-empty` flag was removed. In this version, repo-info no longer\n>    returns a default set of data when called without parameters. However, an\n>    `--all` flag is planned for a future version.\n> - The `plaintext` format was replaced by a null-terminated format, following\n>    the syntax of `git config --list -z`.\n\nThese all look like good changes. Looking through this series I do think \nthat it would be more readable and maintainable if you adopted the table \ndrive approach suggested by Junio. That way we would avoid the nested \nswitch statements and each piece of information only needs to be \nretrieved once rather that having to retrieve it separately for the JSON \nand plaintext output. Below is a sketch of might look. Each key is \nhandled by a callback and we have a table that maps key names to \ncallbacks. For the json output we use one json writer per category to \nbuild output for each category.\n\nBest Wishes\n\nPhillip\n\n\nenum format {\n\tFORMAT_JSON,\n\tFORMAT_PLAINTEXT,\n};\n\nenum category {\n\tCATEGORY_LAYOUT,\n};\n\nstatic const char *category_name[] = {\n\t[CATEGORY_LAYOUT] = \"layout\",\n}\n\nstruct context {\n\tstruct json_writer *writer[ARRAY_SIZE(category_name)];\n\tenum format format;\n};\n\nstatic struct ensure_writer(struct context ctx, enum category cat)\n{\n\tstruct json_writer *writer = ctx.writer[cat];\n\n\tif (!writer) {\n\t\twriter = xmalloc(sizeof(*writer));\n\n\t\tjw_init(writer);\n\t\tjw_object_begin(writer, 1);\n\t\tctx->writer[cat] = writer;\n\t}\n\n\treturn writer;\n}\n\nstatic void handle_layout_bare(struct context ctx)\n{\n\tint bare = ...;\n\t\n\tif (ctx->format == FORMAT_JSON) {\n\t\tstruct json_writer *writer =\n\t\t\tensure_writer(ctx, CATEGORY_LAYOUT);\n\n\t\tjw_object_bool(writer, \"layout.bare\", bare);\n\t} else {\n\t\tprintf(\"%s\\n%s\\c\", \"layout.bare\", bare ? \"true\", \"false\", '\\0');\n\t}\n}\n\nstatic int cmd_repo_info(int argc, const char **argv, sturct repo *r)\n{\n\tstruct context = { 0 };\n\tstruct {\n\t\tconst char *key;\n\t\tvoid (*cb)(struct *ctx);\n\t} handler = {\n\t\t{ \"layout.bare\", handle_layout_bare, },\n\t};\n\n\t/* parse options */\n\n\tfor (int i = 0; i < argc; i++) {\n\t\t/* TODO use bsearch()? */\n\t\tfor (size_t j = 0; j < ARRAY_SIZE(handler); j++) {\n\t\t\tif (!strcmp(argv[i], handler[j].key) {\n\t\t\t\thandler[j].cb(&ctx);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (ctx->format == JSON) {\n\t\tstruct json_writer writer = JSON_WRITER_INIT;\n\t\tjw_object_begin(&writer, 1);\n\t\tfor (size_t i = 0; i < ARRAY_SIZE(category); i++) {\n\t\t\tif (ctx.writer[i]) {\n\t\t\t\tjw_object_end(ctx.writer[i]);\n\t\t\t\tjw_object_sub_obj(&writer,\n\t\t\t\t\t\t  category_name[i],\n\t\t\t\t\t\t  ctx.writer[i]);\n\t\t\t}\n\t\t}\n\t\tjw_object_end(&writer);\n\t\tfputs(writer.buf.buf);\n\t}\n\n\tcontext_release(&ctx);\n\t\n\treturn 0;\n}\n\n"},{"id":"521561","messageId":"C1991C27-9F57-47F7-A622-B3777BCFACCA@gmail.com","threadId":"63618","inReplyTo":"c1f871ec-96a0-4dbc-b84b-4add36bec682@gmail.com","subject":"Re: [GSoC RFC PATCH v3 0/5] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-08T19:27:26Z","receivedAt":"2025-07-08T19:27:42Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Hi Lucas\n\nHi, Phillip!\n\n> These all look like good changes. Looking through this series I do think that\n> it would be more readable and maintainable if you adopted the table drive\n> approach suggested by Junio.\n\nToday I had a meeting with my mentors (Karthik and Patrick), and yeah, this is\nsomething that I'll change in v4.\n\nReturning the keys in the same order as they were requested in the\nnull-terminated format is not so useful now that I'm keeping the keys in the\noutput. Dropping this assumption allows me to make this code simpler.\n\n> That way we would avoid the nested switch statements and each piece of\n> information only needs to be retrieved once rather that having to retrieve it\n> separately for the JSON and plaintext output. Below is a sketch of might look.\n> Each key is handled by a callback and we have a table that maps key names to\n> callbacks. For the json output we use one json writer per category to build\n> output for each category.\n\nAt first glance it looks good to me. It still seems to me that it needs to\nsomehow treat duplicated keys.\n\n\n\n"},{"id":"521711","messageId":"vygdkwopfzrbdpxpxebnq3xdlg3ow4i3w5y5evduae2zuelqcn@la2dikht2qrf","threadId":"63618","inReplyTo":"aGtiyGW4MIYUw3Ed@pks.im","subject":"Re: [GSoC RFC PATCH v2 1/7] repo-info: declare the repo-info command","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-09T20:05:34Z","receivedAt":"2025-07-09T20:11:11Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/07 08:01AM, Patrick Steinhardt wrote:\n> On Fri, Jul 04, 2025 at 06:40:11PM -0300, Lucas Seiki Oshiro wrote:\n> > > Would it make sense to maybe have such whole-repo commands\n> > > grouped together in a `git repo` top-level command? E.g. `git repo info`\n> > > for your command, `git repo size` to gather information about the repo\n> > > size.\n> > \n> > It seems to be very nice for me! In fact, this being a home also for\n> > statistics is something I considered while writing the first versions of\n> > my GSoC proposal.\n> > \n> > And what about merging the two codes into a single API? Something like:\n> > \n> > ```\n> > git repo-info layout.bare references.format survey.commit-count\n> > {\n> >   \"layout\": {\n> >     \"bare\": true\n> >   },\n> >   \"references\": {\n> >     \"format\": \"files\"\n> >   },\n> >   \"survey\": {\n> >     \"commit-count\": 42\n> >   }\n> > }\n> > \n> > ?\n> \n> We could in theory do that. But there's two things we need to be\n> cautious about:\n> \n>   1. We should be mindful about what specifically this tool is about. It\n>      shouldn't become the next tool that does way too many different\n>      things.\n> \n>   2. One of the idea of git-survey(1) is to eventually replace\n>      git-sizer(1). This will require very specific presentation formats\n>      that aren't really compatible with any of the other information.\n> \n> Out of these two I think the second item is the more important one why\n> git-survey(1) should exist as a standalone tool, either as a top-level\n> command or as a subcommand.\n\nAs Patrick mentioned, the focus for git-survey(1) is to be an eventual\nsubstitute for git-sizer(1). For the initial implementation I was\nimagining a simple plaintext format that outputs key/value pairs and\nlooks something like the following example:\n\n  references.branches.count=15\n  references.tags.count=2\n  references.remotes.count=5\n  references.others.count=1\n  objects.commits.count=50\n  objects.commits.total_size=1234567\n  objects.commits.max_size.oid=1817dc08b8ea00fce4cd1fb6bc75713ad00a74d3\n  objects.commits.max_size.size=1234\n  objects.commits.max_parents.oid=1817dc08b8ea00fce4cd1fb6bc75713ad00a74d3\n  objects.commits.max_parents.count=8\n  objects.trees.count=100\n  objects.trees.total_size=12345\n  objects.trees.total_tree_entries=999\n  objects.trees.max_tree_entries.oid=1817dc08b8ea00fce4cd1fb6bc75713ad00a74d3\n  objects.trees.max_tree_entries.count=99\n  objects.blobs.count=142\n  objects.blobs.total_size=99999999\n  objects.blobs.max_size.oid=1817dc08b8ea00fce4cd1fb6bc75713ad00a74d3\n  objects.blobs.max_size.size=999999\n  objects.tags.count=1\n  repo.max_depth=999\n  <etc...>\n\nThe command will also need to eventually support other output formats,\nnamely a more human friendly table format that provides something\nsimilar to git-sizer(1). As layed out above, this looks like it could\nalso work well with the git-repo-info(1) JSON format. This makes me\nwonder if we should add this functionality as a separate flag for\ngit-repo-info(1). Maybe something like `--stats` and append the info do\nthe output. If we want a more clear distiction though, we could\nimplement this as a separate subcommand.\n\nFor a more human-readable format, maybe we could still implement a\nstandalone git-survey(1) that is more of a porcelain command and uses\ngit-repo-info(1) under the hood. I think the other information such as\nreference format and object format may be useful to provide in\ngit-survey(1) output.\n\n> > During our meetings, Karthik suggested (I'm planning to it later) to also\n> > allow to request an entire category instead of only the fields. Then, this\n> > would also be possible:\n> > \n> > ```\n> > $ git repo-info survey\n> > {\n> >   \"survey\": {\n> >     \"commit-count\": 42,\n> >     \"blob-count\": 1234\n> > }\n> > ```\n> \n> It raises another question though: if we ever were to add `--all` we'll\n> need to step a bit careful about what kind of information we add to this\n> tool. All of the information proposed so far can be computed rather\n> trivially. But computing repository sizes has way higher computational\n> complexity and may easily take seconds, maybe even minutes in large\n> repositories.\n> \n> That to me further points into the direction of giving those two tools a\n> common top-level command (`git repo info`, `git repo survey`), but to\n> not mix concerns too much with one another.\n\nGetting the info for git-survey(1) is certainly more computationally\ncomplex so there should be a way to run the command without performing\nthe more expensive checks if the user doesn't want them. At the same\ntime, I think it may be nice to have a way for a user to request a dump\nof \"interesting\" repository info via a single command.\n\n> > But I don't know what are Justin's plans for git-survey, if it would be a\n> > porcelain command for showing those stats to the user of if it is targeted\n> > for being parsed like this `repo-info`.\n\nI think the intent for git-survey was to provide a more porcelain\ncommand to display interesting repository stats to the user, but also\nprovide an option to print in a machine-parsable format. I like the idea\nof computing everything as part of git-repo-info though. This could\nallow a standalone git-survey to focus on just being a human-friendly\nporcelain command. For scripted use-cases, users could then just use\ngit-repo-info.\n\n-Justin\n"},{"id":"521750","messageId":"35ba62ab-19c3-4092-aa7c-9043b898e284@gmail.com","threadId":"63618","inReplyTo":"C1991C27-9F57-47F7-A622-B3777BCFACCA@gmail.com","subject":"Re: [GSoC RFC PATCH v3 0/5] repo-info: add new command for retrieving repository info","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-07-10T13:15:33Z","receivedAt":"2025-07-10T13:15:36Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Lucas\n\nOn 08/07/2025 20:27, Lucas Seiki Oshiro wrote:\n> \n>> These all look like good changes. Looking through this series I do think that\n>> it would be more readable and maintainable if you adopted the table drive\n>> approach suggested by Junio.\n> \n> Today I had a meeting with my mentors (Karthik and Patrick), and yeah, this is\n> something that I'll change in v4.\n\nThat's great\n\n> Returning the keys in the same order as they were requested in the\n> null-terminated format is not so useful now that I'm keeping the keys in the\n> output. Dropping this assumption allows me to make this code simpler.\n> \n>> That way we would avoid the nested switch statements and each piece of\n>> information only needs to be retrieved once rather that having to retrieve it\n>> separately for the JSON and plaintext output. Below is a sketch of might look.\n>> Each key is handled by a callback and we have a table that maps key names to\n>> callbacks. For the json output we use one json writer per category to build\n>> output for each category.\n> \n> At first glance it looks good to me. It still seems to me that it needs to\n> somehow treat duplicated keys.\n\nWe can detect duplicate keys by adding an array\n\n\tchar seen_key[ARRAY_SIZE(handle)];\n\nand checking to see if we've already set `seen_key[j]` in the inner \nloop. We should also detect invalid keys which we can do by setting a \nflag when we find the key or by moving the declaration of `j` into the \nouter loop and checking that `j < ARRAY_SIZE(handle)` at the end of the \ninner loop.\n\nThanks\n\nPhillip\n\n"},{"id":"521835","messageId":"0FEC4502-867F-4B9C-B752-376EF9464983@gmail.com","threadId":"63618","inReplyTo":"20250706231938.16113-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v3 0/5] repo-info: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-11T17:13:31Z","receivedAt":"2025-07-11T17:13:47Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hello again!\n\nI just had a meeting with Justin, especially focused on joining this `repo-info`\ncommand and his `survey` command [1] under the same command (perhaps called \n`git repo`).\n\nSome highlights of our discussion:\n\n1. How would this integration be done? Making this `git repo` command only as a\n   house for two different subcommands, or making it a common interface for our\n   work. An argument for separated subcommands is that `repo-info` is a light\n   command, while `survey` is more computationally expensive. An argument for\n   having a common interface is having a standard format for requesting and\n   retrieving data from both sources.\n\n2. A solution for 1. would be keeping the idea of having `repo-info` and\n   `survey` as two subcommands (perhaps `git repo info` and `git repo stats`),\n   following the same output format. This would also make room for a third\n   command which would return data from both commands. Then `git repo` would be\n   a plumbing command (`git survey` is more porcelain-ish), and its machinery\n   could be used by a separate porcelain command for formatting its output in a\n   more human-readable way.\n\n3. Justin asked me about \"why JSON?\". And yeah, to be honest I'm using JSON\n   because it was listed in the GSoC idea of a machine-readable format that could\n   be easily parsed by other applications. Given that this would be (as far as I\n   remember) the only git command that outputs JSON, it would be out of place,\n   while the other format (null-terminated) is easier to manipulate (e.g. JSON\n   has Unicode issues mentioned by Phillip) and follows an already used syntax\n   (the same as `git config --list -z`). This way, it seems to me that dropping\n   JSON is the way to go.\n\n\nTo sum up, we'll end with a `git repo` command with two (or three) subcommands\nthat output their data in the same format (the null-terminated format).\n\nThen, you can skip reviewing this v3. Instead, I would like to ask you for\ncomments about this new direction.\n\nThanks!\n\n[1] https://gitlab.com/gitlab-org/git/-/issues/529#note_2585264408"},{"id":"521836","messageId":"eze7lvd6wvvbpwa4qqst4c5ctpjdxyaluacxk2ljvzyejzol3s@tob3cyvthnp2","threadId":"63618","inReplyTo":"0FEC4502-867F-4B9C-B752-376EF9464983@gmail.com","subject":"Re: [GSoC RFC PATCH v3 0/5] repo-info: add new command for retrieving repository info","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-11T17:37:48Z","receivedAt":"2025-07-11T17:43:28Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/11 02:13PM, Lucas Seiki Oshiro wrote:\n> 1. How would this integration be done? Making this `git repo` command only as a\n>    house for two different subcommands, or making it a common interface for our\n>    work. An argument for separated subcommands is that `repo-info` is a light\n>    command, while `survey` is more computationally expensive. An argument for\n>    having a common interface is having a standard format for requesting and\n>    retrieving data from both sources.\n> \n> 2. A solution for 1. would be keeping the idea of having `repo-info` and\n>    `survey` as two subcommands (perhaps `git repo info` and `git repo stats`),\n>    following the same output format. This would also make room for a third\n>    command which would return data from both commands. Then `git repo` would be\n>    a plumbing command (`git survey` is more porcelain-ish), and its machinery\n>    could be used by a separate porcelain command for formatting its output in a\n>    more human-readable way.\n\nFor some additional context, Stolee sent an RFC series[1] which proposed\nthe git-survey(1) command as a native tool to complement git-sizer(1) a\nlittle while ago and I've been interested in pushing this topic forward.\n\nAs a first step, I think we could introduce as command, such as `git\nrepo stats`, which focuses on computing \"interesting\" stats about a\nrepository similar to git-sizer(1). The output format for this would be\nsimple key/value pairs for now similar to what Lucas has proposed in\ngit-repo-info(1).\n\nAs there are similarities in command scope with git-repo-info(1), I\nthink it would make sense to group these operations together under the\nsame top-level command.\n\n-Justin\n\n[1] 7d43a1634bbe2d2efa96a806e3de1f1fd480041b.1725935335.git.gitgitgadget@gmail.com\n"},{"id":"521925","messageId":"20250714235231.10137-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-14T23:52:27Z","receivedAt":"2025-07-14T23:52:40Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi again!\n\nThis fourth version is basically a re-write after the discussion with Patrick,\nKarthik and Justin about a future plan of making git-repo-info and git-survey\n[1] a single command with two subcommands.\n\nThe main difference between repo-info and survey is that repo-info retrieves\nmetadata while survey returns metrics about the repository. This means that\nrepo-info is really light compared to survey.\n\nThis patch, then:\n\n- Renames the command to `repo` instead of `repo-info`. All the functionality\n  of `repo-info` will now be under `repo info`. The functionality of `survey`\n  will be moved to another subcommand of `git repo`.\n\n- Removes the JSON support. Given that after the previous feedback we already\n  have a nice machine-readable format for outputting this data, JSON would not\n  be so useful as it seemed to be at first (when the \"other format\" was just\n  returning the values without the keys). This makes the code far more simpler,\n  as we don't need to deal with the details of both formats.\n\n- Uses a simpler representation of the fields, based in their keys instead of\n  declaring multiple enums and using nested switches. This new solution is\n  based in a table mapping the keys and the callbacks for retrieving the data.\n\n- Provide a simple infrastructure for extending with the second command.\n\nGiven that this v4 is almost a rewrite, I think it isn't worth to send a\nrange-diff.\n\nThanks!\n\n[1] https://gitlab.com/gitlab-org/git/-/merge_requests/369\n\nLucas Seiki Oshiro (4):\n  repo: declare the repo command\n  repo: add the field references.format\n  repo: add field layout.bare\n  repo: add field layout.shallow\n\n .gitignore                  |   1 +\n Documentation/git-repo.adoc |  63 ++++++++++++++++\n Documentation/meson.build   |   1 +\n Makefile                    |   1 +\n builtin.h                   |   1 +\n builtin/repo.c              | 146 ++++++++++++++++++++++++++++++++++++\n command-list.txt            |   1 +\n git.c                       |   1 +\n meson.build                 |   1 +\n t/meson.build               |   1 +\n t/t1900-repo.sh             |  75 ++++++++++++++++++\n 11 files changed, 292 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n create mode 100755 t/t1900-repo.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521926","messageId":"20250714235231.10137-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250714235231.10137-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v4 1/4] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-14T23:52:28Z","receivedAt":"2025-07-14T23:52:43Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, `git rev-parse` covers a wide range of functionality not\ndirectly related to parsing revisions, as its name says. Over time,\nmany features like parsing datestrings, options, paths, and others\nwere added to it because there wasn't a more appropriated command\nto place them.\n\nCreate a new Git command called `repo`. `git repo` will be the main\ncommand for obtaining the information about a repository (such as\nmetadata and metrics), returning them in a machine readable format\nfollowing the syntax \"field<LF>value<NUL>\".\n\nAlso declare a subcommand for `repo` called `info`. `git repo info`\nwill bring the functionality of retrieving repository-related\ninformation currently returned by `rev-parse`.\n\nAlso add entries for this new command in:\n\n- the build files (Makefile, meson.build, Documentation/meson.build)\n- builtin.h\n- git.c\n- .gitignore\n- command-list.txt\n- Documentation\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore                  |  1 +\n Documentation/git-repo.adoc | 54 +++++++++++++++++++++++++++++++++++++\n Documentation/meson.build   |  1 +\n Makefile                    |  1 +\n builtin.h                   |  1 +\n builtin/repo.c              | 38 ++++++++++++++++++++++++++\n command-list.txt            |  1 +\n git.c                       |  1 +\n meson.build                 |  1 +\n 9 files changed, 99 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..1803023427 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nnew file mode 100644\nindex 0000000000..6f8fe3f6ea\n--- /dev/null\n+++ b/Documentation/git-repo.adoc\n@@ -0,0 +1,54 @@\n+git-repo(1)\n+===========\n+\n+NAME\n+----\n+git-repo - Retrieve information about a repository\n+\n+SYNOPSIS\n+--------\n+[synopsis]\n+git repo info [<key>...]\n+\n+DESCRIPTION\n+-----------\n+Retrieve information about the current repository in a machine-readable format.\n+\n+`git repo` will be the primary tool to query repository-specific information,\n+such as metadata that currently can also be done by calling `git rev-parse` (see\n+linkgit:git-rev-parse[1]). `git repo` doesn't query information unrelated to the\n+current repository or that is already retrieved by a specialized command, for\n+example, `git config` (see linkgit:git-config[1]) or `git var` (see\n+linkgit:git-var[1]).\n+\n+This command returns the retrieved data following a null-terminated format with\n+this syntax:\n++\n+----------------\n+key1<LF>value1<NUL>\n+key2<LF>value2<NUL>\n+...\n+----------------\n++\n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n+COMMANDS\n+--------\n+info [<key>...]::\n+\tRetrieve metadata-related information about the current repository. Only\n+\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n+\tsection below).\n+\n+INFO KEYS\n+---------\n+\n+The set of data that `git repo` can return is grouped into the following\n+categories:\n+\n+SEE ALSO\n+--------\n+linkgit:git-rev-parse[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 2fe1a1369d..1ebdd57789 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -116,6 +116,7 @@ manpages = {\n   'git-repack.adoc' : 1,\n   'git-replace.adoc' : 1,\n   'git-replay.adoc' : 1,\n+  'git-repo.adoc' : 1,\n   'git-request-pull.adoc' : 1,\n   'git-rerere.adoc' : 1,\n   'git-reset.adoc' : 1,\ndiff --git a/Makefile b/Makefile\nindex 5f7dd79dfa..9dce446309 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1306,6 +1306,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..e6458e6fb9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo.c b/builtin/repo.c\nnew file mode 100644\nindex 0000000000..a1787a3cc5\n--- /dev/null\n+++ b/builtin/repo.c\n@@ -0,0 +1,38 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+static int repo_info(int argc UNUSED,\n+\t\t     const char **argv UNUSED,\n+\t\t     const char *prefix UNUSED,\n+\t\t     struct repository *repo UNUSED)\n+{\n+\treturn 0;\n+}\n+\n+int cmd_repo(int argc,\n+\t     const char **argv,\n+\t     const char *prefix,\n+\t     struct repository *repo)\n+{\n+\tparse_opt_subcommand_fn *fn = NULL;\n+\tconst char *const repo_usage[] = {\n+\t\t\"git repo info\",\n+\t\tNULL\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\tif (fn) {\n+\t\treturn fn(argc, argv, prefix, repo);\n+\t} else {\n+\t\tif (argc) {\n+\t\t\terror(_(\"unknown subcommand: `%s'\"), argv[0]);\n+\t\t\tusage_with_options(repo_usage, options);\n+\t\t}\n+\t\treturn 1;\n+\t}\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex b7ade3ab9f..1b0bdee00d 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -164,6 +164,7 @@ git-remote                              ancillarymanipulators           complete\n git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-replay                              plumbingmanipulators\n+git-repo                                plumbinginterrogators\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           history\ndiff --git a/git.c b/git.c\nindex 07a5fe39fb..8290d8b8c8 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo\", cmd_repo, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 9579377f3d..15d3e3701f 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521927","messageId":"20250714235231.10137-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250714235231.10137-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-14T23:52:29Z","receivedAt":"2025-07-14T23:52:46Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduce the new subcommand\ngit-repo-info.\n\nThe flag `--show-ref-format` from git-rev-parse is used for retrieving\nthe reference format (i.e. `files` or `reftable`). This way, it is\nused for querying repository metadata, fitting in the purpose of\ngit-repo-info.\n\nThen, add a new field `references.format` to the repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  4 ++\n builtin/repo.c              | 92 +++++++++++++++++++++++++++++++++++--\n t/meson.build               |  1 +\n t/t1900-repo.sh             | 47 +++++++++++++++++++\n 4 files changed, 140 insertions(+), 4 deletions(-)\n create mode 100755 t/t1900-repo.sh\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 6f8fe3f6ea..b7af6f45a4 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -45,6 +45,10 @@ INFO KEYS\n The set of data that `git repo` can return is grouped into the following\n categories:\n \n+`references`::\n+Reference-related data:\n+* `format`: the reference storage format, either `files` or `reftable`.\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex a1787a3cc5..dcda0d6d61 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,11 +1,95 @@\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"strbuf.h\"\n+#include \"refs.h\"\n \n-static int repo_info(int argc UNUSED,\n-\t\t     const char **argv UNUSED,\n+typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n+\n+struct field {\n+\tconst char *key;\n+\tadd_field_fn *add_field_callback;\n+};\n+\n+static void add_string(struct strbuf *buf,\n+\t\t       const char *key, const char *value)\n+{\n+\tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n+}\n+\n+static void add_references_format(struct strbuf *buf,\n+\t\t\t\t  struct repository *repo)\n+{\n+\tadd_string(buf, \"references.format\",\n+\t\t   ref_storage_format_to_name(repo->ref_storage_format));\n+}\n+\n+// repo_info_fields keys should be in lexicographical order\n+static const struct field repo_info_fields[] = {\n+\t{\"references.format\", add_references_format},\n+};\n+\n+static int repo_info_fields_cmp(const void *va, const void *vb)\n+{\n+\tconst struct field *a = va;\n+\tconst struct field *b = vb;\n+\n+\treturn strcmp(a->key, b->key);\n+}\n+\n+static add_field_fn *get_append_callback(const char *key) {\n+\tconst struct field search_key = {key, NULL};\n+\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n+\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n+\t\t\t\t\t    sizeof(struct field),\n+\t\t\t\t\t    repo_info_fields_cmp);\n+\treturn found ? found->add_field_callback : NULL;\n+}\n+\n+static int qsort_strcmp(const void *va, const void *vb)\n+{\n+\tconst char *a = *(const char **)va;\n+\tconst char *b = *(const char **)vb;\n+\n+\treturn strcmp(a, b);\n+}\n+\n+static void print_fields(int argc, const char **argv, struct repository *repo) {\n+\tconst char *last = \"\";\n+\tstruct strbuf buf;\n+\tstrbuf_init(&buf, 256);\n+\n+\tQSORT(argv, argc, qsort_strcmp);\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tadd_field_fn *callback;\n+\t\tconst char *key = argv[i];\n+\n+\t\tif (!strcmp(key, last))\n+\t\t\tcontinue;\n+\n+\t\tcallback = get_append_callback(key);\n+\n+\t\tif (!callback) {\n+\t\t\terror(\"key %s not found\", key);\n+\t\t\tstrbuf_release(&buf);\n+\t\t\texit(1);\n+\t\t}\n+\n+\t\tcallback(&buf, repo);\n+\t\tlast = key;\n+\t}\n+\n+\tfwrite(buf.buf, 1, buf.len, stdout);\n+\tstrbuf_release(&buf);\n+}\n+\n+static int repo_info(int argc,\n+\t\t     const char **argv,\n \t\t     const char *prefix UNUSED,\n-\t\t     struct repository *repo UNUSED)\n+\t\t     struct repository *repo)\n {\n+\n+\tprint_fields(argc - 1 , argv + 1, repo);\n \treturn 0;\n }\n \n@@ -16,7 +100,7 @@ int cmd_repo(int argc,\n {\n \tparse_opt_subcommand_fn *fn = NULL;\n \tconst char *const repo_usage[] = {\n-\t\t\"git repo info\",\n+\t\t\"git repo info [<key>...]\",\n \t\tNULL\n \t};\n \tstruct option options[] = {\ndiff --git a/t/meson.build b/t/meson.build\nindex 1af289425d..8693e6abc4 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -245,6 +245,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nnew file mode 100755\nindex 0000000000..b80fc6b78b\n--- /dev/null\n+++ b/t/t1900-repo.sh\n@@ -0,0 +1,47 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+\n+. ./test-lib.sh\n+\n+# Test if a field is correctly returned in the null-terminated format\n+#\n+# Usage: test_repo_info <label> <init command> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init command: a command that creates a repository called 'repo', configured\n+#      accordingly to what is being tested\n+#   key: the key of the field that is being tested\n+#   expected value: the value that the field should contain\n+test_repo_info () {\n+\tlabel=$1\n+\tinit_command=$2\n+\tkey=$3\n+\texpected_value=$4\n+\n+\ttest_expect_success \"$label\" '\n+\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\teval \"$init_command\" &&\n+\t\techo \"$expected_value\" | lf_to_nul >expected &&\n+\t\tgit -C repo repo info \"$key\" >output &&\n+\t\ttail -n 1 output >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n+\n+test_repo_info 'ref format files is retrieved correctly' '\n+\tgit init --ref-format=files repo' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' '\n+\tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n+\n+test_expect_success \"only one value is returned if the same key is requested twice\" '\n+\techo \"references.format\" > expected &&\n+\tgit rev-parse --show-ref-format > ref-format &&\n+\tlf_to_nul <ref-format >>expected &&\n+\tgit repo info references.format references.format > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521928","messageId":"20250714235231.10137-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250714235231.10137-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v4 3/4] repo: add field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-14T23:52:30Z","receivedAt":"2025-07-14T23:52:49Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag --is-bare-repository from git-rev-parse is used for retrieving\nwhether the current repository is bare. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field layout.bare to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  4 ++++\n builtin/repo.c              | 17 +++++++++++++++++\n t/t1900-repo.sh             |  6 ++++++\n 3 files changed, 27 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex b7af6f45a4..db185c5c91 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -49,6 +49,10 @@ categories:\n Reference-related data:\n * `format`: the reference storage format, either `files` or `reftable`.\n \n+`layout`::\n+Information about the how the current repository is represented:\n+* `bare`: `true` if this is a bare repository, otherwise `false`.\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex dcda0d6d61..5eefe06918 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,7 +1,10 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n #include \"parse-options.h\"\n #include \"strbuf.h\"\n #include \"refs.h\"\n+#include \"environment.h\"\n \n typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n \n@@ -16,6 +19,13 @@ static void add_string(struct strbuf *buf,\n \tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n }\n \n+static void add_bool(struct strbuf *buf,\n+\t\t     const char *key, const int value)\n+{\n+\tconst char *output_value = value ? \"true\" : \"false\";\n+\tstrbuf_addf(buf, \"%s\\n%s%c\", key, output_value, '\\0');\n+}\n+\n static void add_references_format(struct strbuf *buf,\n \t\t\t\t  struct repository *repo)\n {\n@@ -23,8 +33,15 @@ static void add_references_format(struct strbuf *buf,\n \t\t   ref_storage_format_to_name(repo->ref_storage_format));\n }\n \n+\n+static void add_layout_bare(struct strbuf *buf, struct repository *repo UNUSED)\n+{\n+\tadd_bool(buf, \"layout.bare\", is_bare_repository());\n+}\n+\n // repo_info_fields keys should be in lexicographical order\n static const struct field repo_info_fields[] = {\n+\t{\"layout.bare\", add_layout_bare},\n \t{\"references.format\", add_references_format},\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex b80fc6b78b..6155e275b5 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -36,6 +36,12 @@ test_repo_info 'ref format files is retrieved correctly' '\n test_repo_info 'ref format reftable is retrieved correctly' '\n \tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' '\n+\tgit init --bare repo' 'layout.bare' 'true'\n+\n test_expect_success \"only one value is returned if the same key is requested twice\" '\n \techo \"references.format\" > expected &&\n \tgit rev-parse --show-ref-format > ref-format &&\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521929","messageId":"20250714235231.10137-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250714235231.10137-1-lucasseikioshiro@gmail.com","subject":"[GSoC RFC PATCH v4 4/4] repo: add field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-14T23:52:31Z","receivedAt":"2025-07-14T23:52:52Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--is-shallow-repository` from git-rev-parse is used for\nretrieving whether the repository is shallow. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field `layout.shallow` to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  1 +\n builtin/repo.c              |  7 +++++++\n t/t1900-repo.sh             | 22 ++++++++++++++++++++++\n 3 files changed, 30 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex db185c5c91..864868993b 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -52,6 +52,7 @@ Reference-related data:\n `layout`::\n Information about the how the current repository is represented:\n * `bare`: `true` if this is a bare repository, otherwise `false`.\n+* `shallow`: `true` if this is a shallow repository, otherwise `false`.\n \n SEE ALSO\n --------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 5eefe06918..d75417a48b 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -5,6 +5,7 @@\n #include \"strbuf.h\"\n #include \"refs.h\"\n #include \"environment.h\"\n+#include \"shallow.h\"\n \n typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n \n@@ -39,9 +40,15 @@ static void add_layout_bare(struct strbuf *buf, struct repository *repo UNUSED)\n \tadd_bool(buf, \"layout.bare\", is_bare_repository());\n }\n \n+static void add_layout_shallow(struct strbuf *buf, struct repository *repo)\n+{\n+\tadd_bool(buf, \"layout.shallow\", is_repository_shallow(repo));\n+}\n+\n // repo_info_fields keys should be in lexicographical order\n static const struct field repo_info_fields[] = {\n \t{\"layout.bare\", add_layout_bare},\n+\t{\"layout.shallow\", add_layout_shallow},\n \t{\"references.format\", add_references_format},\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 6155e275b5..e0e2393247 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -42,6 +42,20 @@ test_repo_info 'bare repository = false is retrieved correctly' '\n test_repo_info 'bare repository = true is retrieved correctly' '\n \tgit init --bare repo' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.shallow' 'false'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' '\n+\tgit init remote &&\n+\tcd remote &&\n+\techo x >x &&\n+\tgit add x &&\n+\tgit commit -m x &&\n+\tcd .. &&\n+\tgit clone --depth 1 \"file://$PWD/remote\" repo &&\n+\trm -rf remote\n+\t' 'layout.shallow' 'true'\n+\n test_expect_success \"only one value is returned if the same key is requested twice\" '\n \techo \"references.format\" > expected &&\n \tgit rev-parse --show-ref-format > ref-format &&\n@@ -50,4 +64,12 @@ test_expect_success \"only one value is returned if the same key is requested twi\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'output is returned correctly when two keys are requested' '\n+\ttest_when_finished \"rm -f expect\" &&\n+\tprintf \"layout.bare\\nfalseQlayout.shallow\\nfalseQ\" >expect &&\n+\tgit repo info layout.shallow layout.bare >output &&\n+\tnul_to_q <output >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"521960","messageId":"aHYuwlWlbkc600Ps@ugly","threadId":"63618","inReplyTo":"20250714235231.10137-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2025-07-15T10:34:42Z","receivedAt":"2025-07-15T10:34:48Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"On Mon, Jul 14, 2025 at 08:52:27PM -0300, Lucas Seiki Oshiro wrote:\n>- Renames the command to `repo` instead of `repo-info`. All the \n>functionality\n>  of `repo-info` will now be under `repo info`. The functionality of `survey`\n>  will be moved to another subcommand of `git repo`.\n>\nthis strikes me as a bad idea, given how established the `repo` tool is.\n\nwithout much thinking and reading prior conversations, i'd go with \n\"query\", because it's a database-like metadata ... query.\nthe obvious followup idea would then be \"meta\", but that suggests that \nit isn't only a read-only command, which i think it is supposed to \nremain?\n"},{"id":"521983","messageId":"CAOLa=ZSgmRK_59BBj4UMhKmksP0WARneOzpLxfMytbeXNzoqug@mail.gmail.com","threadId":"63618","inReplyTo":"20250714235231.10137-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v4 1/4] repo: declare the repo command","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-07-15T11:52:17Z","receivedAt":"2025-07-15T11:52:19Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Currently, `git rev-parse` covers a wide range of functionality not\n> directly related to parsing revisions, as its name says. Over time,\n> many features like parsing datestrings, options, paths, and others\n> were added to it because there wasn't a more appropriated command\n> to place them.\n>\n> Create a new Git command called `repo`. `git repo` will be the main\n> command for obtaining the information about a repository (such as\n> metadata and metrics), returning them in a machine readable format\n> following the syntax \"field<LF>value<NUL>\".\n\nI don't think we should enforce a syntax on the command level, but\nrather we should enforce it on the sub-command level. This way any new\ncommand, let's say `git repo stats` can provide a more suitable output.\nWhich could be a more human readable format mimicking 'git-sizer'.\n\nBy not enforcing on a command level, we leave it extendable.\n\n>\n> Also declare a subcommand for `repo` called `info`. `git repo info`\n> will bring the functionality of retrieving repository-related\n> information currently returned by `rev-parse`.\n>\n> Also add entries for this new command in:\n>\n> - the build files (Makefile, meson.build, Documentation/meson.build)\n> - builtin.h\n> - git.c\n> - .gitignore\n> - command-list.txt\n> - Documentation\n>\n\nNit: While it is good to state what this patch does, this list is\nsimilar to the diffstat below. Is there additional information it is\nproviding?\n\nPerhaps:\n\n  We add the required tests, documentation and build changes to enable\n  usage of this subcommand.\n\nwould suffice?\n\n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  .gitignore                  |  1 +\n>  Documentation/git-repo.adoc | 54 +++++++++++++++++++++++++++++++++++++\n>  Documentation/meson.build   |  1 +\n>  Makefile                    |  1 +\n>  builtin.h                   |  1 +\n>  builtin/repo.c              | 38 ++++++++++++++++++++++++++\n>  command-list.txt            |  1 +\n>  git.c                       |  1 +\n>  meson.build                 |  1 +\n>  9 files changed, 99 insertions(+)\n>  create mode 100644 Documentation/git-repo.adoc\n>  create mode 100644 builtin/repo.c\n>\n> diff --git a/.gitignore b/.gitignore\n> index 04c444404e..1803023427 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -139,6 +139,7 @@\n>  /git-repack\n>  /git-replace\n>  /git-replay\n> +/git-repo\n>  /git-request-pull\n>  /git-rerere\n>  /git-reset\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> new file mode 100644\n> index 0000000000..6f8fe3f6ea\n> --- /dev/null\n> +++ b/Documentation/git-repo.adoc\n> @@ -0,0 +1,54 @@\n> +git-repo(1)\n> +===========\n> +\n> +NAME\n> +----\n> +git-repo - Retrieve information about a repository\n> +\n> +SYNOPSIS\n> +--------\n> +[synopsis]\n> +git repo info [<key>...]\n> +\n> +DESCRIPTION\n> +-----------\n> +Retrieve information about the current repository in a machine-readable format.\n> +\n\nI would keep the description of the command short, to:\n\n  This command retrieve repository level information.\n\nThe `machine-readable` format is a implementation detail of the\n'git-repo info' subcommand.\n\n> +`git repo` will be the primary tool to query repository-specific information,\n> +such as metadata that currently can also be done by calling `git rev-parse` (see\n> +linkgit:git-rev-parse[1]). `git repo` doesn't query information unrelated to the\n> +current repository or that is already retrieved by a specialized command, for\n> +example, `git config` (see linkgit:git-config[1]) or `git var` (see\n> +linkgit:git-var[1]).\n> +\n> +This command returns the retrieved data following a null-terminated format with\n> +this syntax:\n> ++\n> +----------------\n> +key1<LF>value1<NUL>\n> +key2<LF>value2<NUL>\n> +...\n> +----------------\n>\n\nShouldn't this whole section be below the 'info' subcommand?\n\n> ++\n> +THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n> +\n> +COMMANDS\n> +--------\n> +info [<key>...]::\n> +\tRetrieve metadata-related information about the current repository. Only\n> +\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n> +\tsection below).\n> +\n> +INFO KEYS\n> +---------\n> +\n> +The set of data that `git repo` can return is grouped into the following\n> +categories:\n> +\n> +SEE ALSO\n> +--------\n> +linkgit:git-rev-parse[1]\n> +\n> +GIT\n> +---\n> +Part of the linkgit:git[1] suite\n[snip]\n"},{"id":"521984","messageId":"aHZCfynoeMDRdN4X@pks.im","threadId":"63618","inReplyTo":"aHYuwlWlbkc600Ps@ugly","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-15T11:58:55Z","receivedAt":"2025-07-15T11:59:02Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Jul 15, 2025 at 12:34:42PM +0200, Oswald Buddenhagen wrote:\n> On Mon, Jul 14, 2025 at 08:52:27PM -0300, Lucas Seiki Oshiro wrote:\n> > - Renames the command to `repo` instead of `repo-info`. All the\n> > functionality\n> >  of `repo-info` will now be under `repo info`. The functionality of `survey`\n> >  will be moved to another subcommand of `git repo`.\n> > \n> this strikes me as a bad idea, given how established the `repo` tool is.\n\nThe `repo` tool wouldn't be executed as `git repo` though, would it? So\nI'm not sure whether that really is relevant at all. On the other hand\nthough I do see that it might be confusing when you interact with the\n`repo` tool on a daily basis.\n\n> without much thinking and reading prior conversations, i'd go with \"query\",\n> because it's a database-like metadata ... query.\n> the obvious followup idea would then be \"meta\", but that suggests that it\n> isn't only a read-only command, which i think it is supposed to remain?\n\n\"Query\" is way too generic from my point of view, as it doesn't say\n_what_ you query. \"Meta\" might be a bit better even though it still\nloses the information that you act on the repository level, which is a\nbit of a shame.\n\nWe could of course adapt and call it git-repository(1) to avoid any\nconfusion with git-repo(1) and repo(1). It's not like this is a tool\nthat users would typically have to run daily outside of scripts, so I\ndon't think it hurts much to have a longer command name.\n\nPatrick\n"},{"id":"521985","messageId":"aHZChXPTbk_X6xDK@pks.im","threadId":"63618","inReplyTo":"20250714235231.10137-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v4 1/4] repo: declare the repo command","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-15T11:59:01Z","receivedAt":"2025-07-15T11:59:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Jul 14, 2025 at 08:52:28PM -0300, Lucas Seiki Oshiro wrote:\n> Currently, `git rev-parse` covers a wide range of functionality not\n> directly related to parsing revisions, as its name says. Over time,\n> many features like parsing datestrings, options, paths, and others\n> were added to it because there wasn't a more appropriated command\n> to place them.\n> \n> Create a new Git command called `repo`. `git repo` will be the main\n> command for obtaining the information about a repository (such as\n> metadata and metrics), returning them in a machine readable format\n> following the syntax \"field<LF>value<NUL>\".\n> \n> Also declare a subcommand for `repo` called `info`. `git repo info`\n> will bring the functionality of retrieving repository-related\n> information currently returned by `rev-parse`.\n> \n> Also add entries for this new command in:\n> \n> - the build files (Makefile, meson.build, Documentation/meson.build)\n> - builtin.h\n> - git.c\n> - .gitignore\n> - command-list.txt\n> - Documentation\n\nNit: I don't really think it makes sense to have this bulleted list of\nfiles you have changed unless you provide more context. We basically\nalready have a way more accurate version of this list 10 lines down in\nthe diffstat.\n\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> new file mode 100644\n> index 0000000000..6f8fe3f6ea\n> --- /dev/null\n> +++ b/Documentation/git-repo.adoc\n> @@ -0,0 +1,54 @@\n> +git-repo(1)\n> +===========\n> +\n> +NAME\n> +----\n> +git-repo - Retrieve information about a repository\n> +\n> +SYNOPSIS\n> +--------\n> +[synopsis]\n> +git repo info [<key>...]\n> +\n> +DESCRIPTION\n> +-----------\n> +Retrieve information about the current repository in a machine-readable format.\n> +\n> +`git repo` will be the primary tool to query repository-specific information,\n\nI think we should avoid making any promises how this command will evolve\nin the future. I'd rather state what it does right now than to say what\nit will eventually do.\n\n> +such as metadata that currently can also be done by calling `git rev-parse` (see\n> +linkgit:git-rev-parse[1]). `git repo` doesn't query information unrelated to the\n> +current repository or that is already retrieved by a specialized command, for\n> +example, `git config` (see linkgit:git-config[1]) or `git var` (see\n> +linkgit:git-var[1]).\n> +\n> +This command returns the retrieved data following a null-terminated format with\n> +this syntax:\n> ++\n> +----------------\n> +key1<LF>value1<NUL>\n> +key2<LF>value2<NUL>\n> +...\n> +----------------\n> ++\n\nOne of the things I wonder is whether we should by default adapt those\ntools to have human-readable format, e.g. in a way that it can be easily\nadded to git-bugreport(1). This would teach script authors that want to\nuse the command to use `git repo info --format=porcelain` right from the\nstart to have a machine-parseable output, and it would allow us to\niterate on the exact output format.\n\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> new file mode 100644\n> index 0000000000..a1787a3cc5\n> --- /dev/null\n> +++ b/builtin/repo.c\n> @@ -0,0 +1,38 @@\n> +#include \"builtin.h\"\n> +#include \"parse-options.h\"\n> +\n> +static int repo_info(int argc UNUSED,\n> +\t\t     const char **argv UNUSED,\n> +\t\t     const char *prefix UNUSED,\n> +\t\t     struct repository *repo UNUSED)\n> +{\n> +\treturn 0;\n> +}\n> +\n> +int cmd_repo(int argc,\n> +\t     const char **argv,\n> +\t     const char *prefix,\n> +\t     struct repository *repo)\n> +{\n> +\tparse_opt_subcommand_fn *fn = NULL;\n> +\tconst char *const repo_usage[] = {\n> +\t\t\"git repo info\",\n> +\t\tNULL\n> +\t};\n> +\tstruct option options[] = {\n> +\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n> +\n> +\tif (fn) {\n> +\t\treturn fn(argc, argv, prefix, repo);\n> +\t} else {\n> +\t\tif (argc) {\n> +\t\t\terror(_(\"unknown subcommand: `%s'\"), argv[0]);\n> +\t\t\tusage_with_options(repo_usage, options);\n> +\t\t}\n> +\t\treturn 1;\n\nI think we need to print an error `if (!argc)`, as well. Otherwise the\nuser wouldn't know why `git repo` without any argumentsdoesn't do\nanything.\n\nPatrick\n"},{"id":"521986","messageId":"aHZCipKCPIu8mPly@pks.im","threadId":"63618","inReplyTo":"20250714235231.10137-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-15T11:59:06Z","receivedAt":"2025-07-15T11:59:12Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Jul 14, 2025 at 08:52:29PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index a1787a3cc5..dcda0d6d61 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -1,11 +1,95 @@\n>  #include \"builtin.h\"\n>  #include \"parse-options.h\"\n> +#include \"strbuf.h\"\n> +#include \"refs.h\"\n>  \n> -static int repo_info(int argc UNUSED,\n> -\t\t     const char **argv UNUSED,\n> +typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n> +\n> +struct field {\n> +\tconst char *key;\n> +\tadd_field_fn *add_field_callback;\n> +};\n> +\n> +static void add_string(struct strbuf *buf,\n> +\t\t       const char *key, const char *value)\n> +{\n> +\tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n> +}\n> +\n> +static void add_references_format(struct strbuf *buf,\n> +\t\t\t\t  struct repository *repo)\n> +{\n> +\tadd_string(buf, \"references.format\",\n> +\t\t   ref_storage_format_to_name(repo->ref_storage_format));\n> +}\n> +\n> +// repo_info_fields keys should be in lexicographical order\n\nStyle: we don't use '//' comments.\n\n> +static const struct field repo_info_fields[] = {\n> +\t{\"references.format\", add_references_format},\n\nStyle: we tend to have a space between curly braces and their inner\ncontent.\n\n> +static void print_fields(int argc, const char **argv, struct repository *repo) {\n\nStyle: the opening brace for functions should be on their own line.\n\n> +\tconst char *last = \"\";\n> +\tstruct strbuf buf;\n> +\tstrbuf_init(&buf, 256);\n> +\n> +\tQSORT(argv, argc, qsort_strcmp);\n> +\n> +\tfor (int i = 0; i < argc; i++) {\n> +\t\tadd_field_fn *callback;\n> +\t\tconst char *key = argv[i];\n> +\n> +\t\tif (!strcmp(key, last))\n> +\t\t\tcontinue;\n> +\n> +\t\tcallback = get_append_callback(key);\n> +\n> +\t\tif (!callback) {\n> +\t\t\terror(\"key %s not found\", key);\n> +\t\t\tstrbuf_release(&buf);\n> +\t\t\texit(1);\n> +\t\t}\n> +\n> +\t\tcallback(&buf, repo);\n> +\t\tlast = key;\n> +\t}\n> +\n> +\tfwrite(buf.buf, 1, buf.len, stdout);\n> +\tstrbuf_release(&buf);\n\nIs there any reason why the callback appends to a buffer instead of\nprinting the data immediately?\n\nPatrick\n"},{"id":"521989","messageId":"aHZHjkb9Vc5Y_ta9@ugly","threadId":"63618","inReplyTo":"aHZCfynoeMDRdN4X@pks.im","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2025-07-15T12:20:30Z","receivedAt":"2025-07-15T12:20:37Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"On Tue, Jul 15, 2025 at 01:58:55PM +0200, Patrick Steinhardt wrote:\n>The `repo` tool wouldn't be executed as `git repo` though, would it? So\n>I'm not sure whether that really is relevant at all. On the other hand\n>though I do see that it might be confusing when you interact with the\n>`repo` tool on a daily basis.\n>\ni don't think it would be a problem in actual use. i'm more concerned \nabout people talking about it. really kinda a trademark thing.\n\n>\"Query\" is way too generic from my point of view, as it doesn't say\n>_what_ you query. \"Meta\" might be a bit better even though it still\n>loses the information that you act on the repository level, which is a\n>bit of a shame.\n>\nby that logic, almost all git commands are too tersely named.\nbut in practice, a sufficiently suggestive mnemonic is good enough.  \npreferably a verb, because it's a command.\nthe fact that it's now being made into a \"multi-tool\" also kinda \npreempts future collisions.\n"},{"id":"521990","messageId":"CAOLa=ZTTuxq5Xs4M+okK+3t5Rr_MimseQf8TCTrCL4yvq4EN6w@mail.gmail.com","threadId":"63618","inReplyTo":"20250714235231.10137-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-07-15T12:23:36Z","receivedAt":"2025-07-15T12:23:38Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> This commit is part of the series that introduce the new subcommand\n> git-repo-info.\n>\n> The flag `--show-ref-format` from git-rev-parse is used for retrieving\n> the reference format (i.e. `files` or `reftable`). This way, it is\n> used for querying repository metadata, fitting in the purpose of\n> git-repo-info.\n>\n> Then, add a new field `references.format` to the repo-info subcommand\n> containing that information.\n>\n\nNit: s/Then, add/Add\n\n>\n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  Documentation/git-repo.adoc |  4 ++\n>  builtin/repo.c              | 92 +++++++++++++++++++++++++++++++++++--\n>  t/meson.build               |  1 +\n>  t/t1900-repo.sh             | 47 +++++++++++++++++++\n>  4 files changed, 140 insertions(+), 4 deletions(-)\n>  create mode 100755 t/t1900-repo.sh\n>\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index 6f8fe3f6ea..b7af6f45a4 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -45,6 +45,10 @@ INFO KEYS\n>  The set of data that `git repo` can return is grouped into the following\n>  categories:\n>\n> +`references`::\n> +Reference-related data:\n> +* `format`: the reference storage format, either `files` or `reftable`.\n> +\n\nNit: I would omit the '`files` or `reftable`' here, because while this\nis currently true. This might not hold up in the future. So better to\nnot go into the details of the supported systems.\n\n>  SEE ALSO\n>  --------\n>  linkgit:git-rev-parse[1]\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index a1787a3cc5..dcda0d6d61 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -1,11 +1,95 @@\n>  #include \"builtin.h\"\n>  #include \"parse-options.h\"\n> +#include \"strbuf.h\"\n> +#include \"refs.h\"\n>\n> -static int repo_info(int argc UNUSED,\n> -\t\t     const char **argv UNUSED,\n> +typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n> +\n> +struct field {\n> +\tconst char *key;\n> +\tadd_field_fn *add_field_callback;\n> +};\n> +\n> +static void add_string(struct strbuf *buf,\n> +\t\t       const char *key, const char *value)\n> +{\n> +\tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n> +}\n> +\n\nI like the table design used here, makes things much simpler. I do think\nthat each field shouldn't worry about the formatting, in fact, I would\nsay that we can move all of this logic to `print_fields`.\n\nSo each field would only be incharge of providing the output data. Then\n`print_fields` would take the key, the output data and format it as\nneeded. This would also make it much easier to use a new format if\nneeded in the future.\n\n> +static void add_references_format(struct strbuf *buf,\n> +\t\t\t\t  struct repository *repo)\n> +{\n> +\tadd_string(buf, \"references.format\",\n> +\t\t   ref_storage_format_to_name(repo->ref_storage_format));\n> +}\n> +\n> +// repo_info_fields keys should be in lexicographical order\n> +static const struct field repo_info_fields[] = {\n> +\t{\"references.format\", add_references_format},\n> +};\n> +\n> +static int repo_info_fields_cmp(const void *va, const void *vb)\n> +{\n> +\tconst struct field *a = va;\n> +\tconst struct field *b = vb;\n> +\n> +\treturn strcmp(a->key, b->key);\n> +}\n> +\n> +static add_field_fn *get_append_callback(const char *key) {\n> +\tconst struct field search_key = {key, NULL};\n> +\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n> +\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n> +\t\t\t\t\t    sizeof(struct field),\n> +\t\t\t\t\t    repo_info_fields_cmp);\n> +\treturn found ? found->add_field_callback : NULL;\n> +}\n> +\n> +static int qsort_strcmp(const void *va, const void *vb)\n> +{\n> +\tconst char *a = *(const char **)va;\n> +\tconst char *b = *(const char **)vb;\n> +\n> +\treturn strcmp(a, b);\n> +}\n> +\n> +static void print_fields(int argc, const char **argv, struct repository *repo) {\n> +\tconst char *last = \"\";\n> +\tstruct strbuf buf;\n> +\tstrbuf_init(&buf, 256);\n> +\n> +\tQSORT(argv, argc, qsort_strcmp);\n> +\n> +\tfor (int i = 0; i < argc; i++) {\n> +\t\tadd_field_fn *callback;\n> +\t\tconst char *key = argv[i];\n> +\n> +\t\tif (!strcmp(key, last))\n> +\t\t\tcontinue;\n> +\n> +\t\tcallback = get_append_callback(key);\n> +\n> +\t\tif (!callback) {\n> +\t\t\terror(\"key %s not found\", key);\n> +\t\t\tstrbuf_release(&buf);\n> +\t\t\texit(1);\n> +\t\t}\n> +\n> +\t\tcallback(&buf, repo);\n> +\t\tlast = key;\n> +\t}\n> +\n> +\tfwrite(buf.buf, 1, buf.len, stdout);\n> +\tstrbuf_release(&buf);\n> +}\n> +\n> +static int repo_info(int argc,\n> +\t\t     const char **argv,\n>  \t\t     const char *prefix UNUSED,\n> -\t\t     struct repository *repo UNUSED)\n> +\t\t     struct repository *repo)\n>  {\n> +\n> +\tprint_fields(argc - 1 , argv + 1, repo);\n>  \treturn 0;\n>  }\n>\n> @@ -16,7 +100,7 @@ int cmd_repo(int argc,\n>  {\n>  \tparse_opt_subcommand_fn *fn = NULL;\n>  \tconst char *const repo_usage[] = {\n> -\t\t\"git repo info\",\n> +\t\t\"git repo info [<key>...]\",\n\nShouldn't this be part of the previous commit?\n\n>  \t\tNULL\n>  \t};\n>  \tstruct option options[] = {\n> diff --git a/t/meson.build b/t/meson.build\n> index 1af289425d..8693e6abc4 100644\n> --- a/t/meson.build\n> +++ b/t/meson.build\n> @@ -245,6 +245,7 @@ integration_tests = [\n>    't1700-split-index.sh',\n>    't1701-racy-split-index.sh',\n>    't1800-hook.sh',\n> +  't1900-repo.sh',\n>    't2000-conflict-when-checking-files-out.sh',\n>    't2002-checkout-cache-u.sh',\n>    't2003-checkout-cache-mkdir.sh',\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> new file mode 100755\n> index 0000000000..b80fc6b78b\n> --- /dev/null\n> +++ b/t/t1900-repo.sh\n> @@ -0,0 +1,47 @@\n> +#!/bin/sh\n> +\n> +test_description='test git repo-info'\n> +\n> +. ./test-lib.sh\n> +\n> +# Test if a field is correctly returned in the null-terminated format\n> +#\n> +# Usage: test_repo_info <label> <init command> <key> <expected value>\n> +#\n> +# Arguments:\n> +#   label: the label of the test\n> +#   init command: a command that creates a repository called 'repo', configured\n> +#      accordingly to what is being tested\n> +#   key: the key of the field that is being tested\n> +#   expected value: the value that the field should contain\n> +test_repo_info () {\n> +\tlabel=$1\n> +\tinit_command=$2\n> +\tkey=$3\n> +\texpected_value=$4\n> +\n> +\ttest_expect_success \"$label\" '\n> +\t\ttest_when_finished \"rm -rf repo\" &&\n> +\t\teval \"$init_command\" &&\n> +\t\techo \"$expected_value\" | lf_to_nul >expected &&\n> +\t\tgit -C repo repo info \"$key\" >output &&\n> +\t\ttail -n 1 output >actual &&\n> +\t\ttest_cmp expected actual\n> +\t'\n> +}\n> +\n> +test_repo_info 'ref format files is retrieved correctly' '\n> +\tgit init --ref-format=files repo' 'references.format' 'files'\n> +\n> +test_repo_info 'ref format reftable is retrieved correctly' '\n> +\tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n> +\n> +test_expect_success \"only one value is returned if the same key is requested twice\" '\n> +\techo \"references.format\" > expected &&\n> +\tgit rev-parse --show-ref-format > ref-format &&\n> +\tlf_to_nul <ref-format >>expected &&\n> +\tgit repo info references.format references.format > actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_done\n> --\n> 2.39.5 (Apple Git-154)\n"},{"id":"522012","messageId":"xmqqecuhiekv.fsf@gitster.g","threadId":"63618","inReplyTo":"aHYuwlWlbkc600Ps@ugly","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-15T16:49:52Z","receivedAt":"2025-07-15T16:49:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Oswald Buddenhagen <oswald.buddenhagen@gmx.de> writes:\n\n> On Mon, Jul 14, 2025 at 08:52:27PM -0300, Lucas Seiki Oshiro wrote:\n>> - Renames the command to `repo` instead of `repo-info`. All the\n>> functionality\n>>  of `repo-info` will now be under `repo info`. The functionality of `survey`\n>>  will be moved to another subcommand of `git repo`.\n>>\n> this strikes me as a bad idea, given how established the `repo` tool is.\n>\n> without much thinking and reading prior conversations, i'd go with\n> \"query\", because it's a database-like metadata ... query.\n> the obvious followup idea would then be \"meta\", but that suggests that\n> it isn't only a read-only command, which i think it is supposed to\n> remain?\n\n\"git repository query\"\n\"git repository stat\"\n...?\n"},{"id":"522019","messageId":"4z7ho6hg347j425mzkgueywwptynk3fgj5mmmtkua4zbkprvui@taqrlenltxkb","threadId":"63618","inReplyTo":"20250714235231.10137-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v4 1/4] repo: declare the repo command","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-15T18:19:45Z","receivedAt":"2025-07-15T18:25:28Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/14 08:52PM, Lucas Seiki Oshiro wrote:\n> Currently, `git rev-parse` covers a wide range of functionality not\n> directly related to parsing revisions, as its name says. Over time,\n\ns/says/suggests/\n\n> many features like parsing datestrings, options, paths, and others\n> were added to it because there wasn't a more appropriated command\n\ns/appropriated/appropriate/\n\n> to place them.\n> \n> Create a new Git command called `repo`. `git repo` will be the main\n> command for obtaining the information about a repository (such as\n> metadata and metrics), returning them in a machine readable format\n> following the syntax \"field<LF>value<NUL>\".\n> \n> Also declare a subcommand for `repo` called `info`. `git repo info`\n> will bring the functionality of retrieving repository-related\n> information currently returned by `rev-parse`.\n> \n> Also add entries for this new command in:\n> \n> - the build files (Makefile, meson.build, Documentation/meson.build)\n> - builtin.h\n> - git.c\n> - .gitignore\n> - command-list.txt\n> - Documentation\n> \n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  .gitignore                  |  1 +\n>  Documentation/git-repo.adoc | 54 +++++++++++++++++++++++++++++++++++++\n>  Documentation/meson.build   |  1 +\n>  Makefile                    |  1 +\n>  builtin.h                   |  1 +\n>  builtin/repo.c              | 38 ++++++++++++++++++++++++++\n>  command-list.txt            |  1 +\n>  git.c                       |  1 +\n>  meson.build                 |  1 +\n>  9 files changed, 99 insertions(+)\n>  create mode 100644 Documentation/git-repo.adoc\n>  create mode 100644 builtin/repo.c\n> \n> diff --git a/.gitignore b/.gitignore\n> index 04c444404e..1803023427 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -139,6 +139,7 @@\n>  /git-repack\n>  /git-replace\n>  /git-replay\n> +/git-repo\n>  /git-request-pull\n>  /git-rerere\n>  /git-reset\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> new file mode 100644\n> index 0000000000..6f8fe3f6ea\n> --- /dev/null\n> +++ b/Documentation/git-repo.adoc\n> @@ -0,0 +1,54 @@\n> +git-repo(1)\n> +===========\n> +\n> +NAME\n> +----\n> +git-repo - Retrieve information about a repository\n> +\n> +SYNOPSIS\n> +--------\n> +[synopsis]\n> +git repo info [<key>...]\n> +\n> +DESCRIPTION\n> +-----------\n> +Retrieve information about the current repository in a machine-readable format.\n> +\n> +`git repo` will be the primary tool to query repository-specific information,\n> +such as metadata that currently can also be done by calling `git rev-parse` (see\n> +linkgit:git-rev-parse[1]). `git repo` doesn't query information unrelated to the\n> +current repository or that is already retrieved by a specialized command, for\n> +example, `git config` (see linkgit:git-config[1]) or `git var` (see\n> +linkgit:git-var[1]).\n> +\n> +This command returns the retrieved data following a null-terminated format with\n> +this syntax:\n> ++\n> +----------------\n> +key1<LF>value1<NUL>\n> +key2<LF>value2<NUL>\n> +...\n> +----------------\n\nBeing that this patch doesn't yet implement any output for the command,\nmaybe should should hold off on specifying the format.\n\nIn other commands, it is common to see a nul-terminated format toggled\nbehind a `-z` flag. We may want to do something similar here as opposed\nto being the default.\n\n-Justin\n"},{"id":"522021","messageId":"y3t46fgy6vun6qlxb2fq7n3tcq6zkhfxeoluratzw7jj73ksyn@ggyxtxbgdknu","threadId":"63618","inReplyTo":"aHZChXPTbk_X6xDK@pks.im","subject":"Re: [GSoC RFC PATCH v4 1/4] repo: declare the repo command","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-15T18:38:28Z","receivedAt":"2025-07-15T18:44:11Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/15 01:59PM, Patrick Steinhardt wrote:\n> On Mon, Jul 14, 2025 at 08:52:28PM -0300, Lucas Seiki Oshiro wrote:\n> > +This command returns the retrieved data following a null-terminated format with\n> > +this syntax:\n> > ++\n> > +----------------\n> > +key1<LF>value1<NUL>\n> > +key2<LF>value2<NUL>\n> > +...\n> > +----------------\n> > ++\n> \n> One of the things I wonder is whether we should by default adapt those\n> tools to have human-readable format, e.g. in a way that it can be easily\n> added to git-bugreport(1). This would teach script authors that want to\n> use the command to use `git repo info --format=porcelain` right from the\n> start to have a machine-parseable output, and it would allow us to\n> iterate on the exact output format.\n\nFor this subcommand (and the future \"stats\" one), I think the ouput\nshould default to a simple <key=value> form for each line and use a `-z`\nflag to add support for a nul-delimited mode. This way we still have a\ndefault view that a human can feasible consume while also supporting a\nmore machine friendly format in a manner consistent with other commands.\n\nFor a more decorated view of the data, something akin to git-sizer(1),\nmaybe we could introduce a separate subcommand in the future like `git\nrepo summary`. This could allow us to implement other information such\nas \"level of concern\" separately and also group the data in a way that\nis easier to digest.\n\n-Justin\n"},{"id":"522037","messageId":"mgdervgp34m6ipfbodsfn7cztcl7gdeggzemfgivzvuyk7qtba@wdijebkuioxg","threadId":"63618","inReplyTo":"20250714235231.10137-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-15T19:15:07Z","receivedAt":"2025-07-15T19:20:50Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/14 08:52PM, Lucas Seiki Oshiro wrote:\n> This commit is part of the series that introduce the new subcommand\n\ns/introduce/introduces/\n\n> git-repo-info.\n> \n> The flag `--show-ref-format` from git-rev-parse is used for retrieving\n> the reference format (i.e. `files` or `reftable`). This way, it is\n> used for querying repository metadata, fitting in the purpose of\n> git-repo-info.\n> \n> Then, add a new field `references.format` to the repo-info subcommand\n> containing that information.\n> \n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  Documentation/git-repo.adoc |  4 ++\n>  builtin/repo.c              | 92 +++++++++++++++++++++++++++++++++++--\n>  t/meson.build               |  1 +\n>  t/t1900-repo.sh             | 47 +++++++++++++++++++\n>  4 files changed, 140 insertions(+), 4 deletions(-)\n>  create mode 100755 t/t1900-repo.sh\n> \n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index 6f8fe3f6ea..b7af6f45a4 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -45,6 +45,10 @@ INFO KEYS\n>  The set of data that `git repo` can return is grouped into the following\n>  categories:\n>  \n> +`references`::\n> +Reference-related data:\n> +* `format`: the reference storage format, either `files` or `reftable`.\n> +\n>  SEE ALSO\n>  --------\n>  linkgit:git-rev-parse[1]\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index a1787a3cc5..dcda0d6d61 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -1,11 +1,95 @@\n>  #include \"builtin.h\"\n>  #include \"parse-options.h\"\n> +#include \"strbuf.h\"\n> +#include \"refs.h\"\n>  \n> -static int repo_info(int argc UNUSED,\n> -\t\t     const char **argv UNUSED,\n> +typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n> +\n> +struct field {\n> +\tconst char *key;\n> +\tadd_field_fn *add_field_callback;\n> +};\n> +\n> +static void add_string(struct strbuf *buf,\n> +\t\t       const char *key, const char *value)\n> +{\n> +\tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n> +}\n\nAny reason we add each key/value pair to a buffer instead of just\nprinting it?\n\nAlso, as mentioned in a comment for the previous patch, maybe we should\nsupport printing two output modes. For the default output, maybe a\nsimple `<key>=<value>\\n` where the any value containing special\ncharacters is quoted via `quote_c_style()`.\n\nA null-terminated output, such as the one proposed in this patch, could\nbe enabled via a `-z` flag similar to how its done in other commands.\n\n-Justin\n"},{"id":"522040","messageId":"o3hgyfke3tq3fjn7bl7iamq34k5kczp52phr2zvidlogcntk2z@dgts2mw2ius6","threadId":"63618","inReplyTo":"aHZCfynoeMDRdN4X@pks.im","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-15T19:36:24Z","receivedAt":"2025-07-15T19:42:07Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/15 01:58PM, Patrick Steinhardt wrote:\n> On Tue, Jul 15, 2025 at 12:34:42PM +0200, Oswald Buddenhagen wrote:\n> > On Mon, Jul 14, 2025 at 08:52:27PM -0300, Lucas Seiki Oshiro wrote:\n> > > - Renames the command to `repo` instead of `repo-info`. All the\n> > > functionality\n> > >  of `repo-info` will now be under `repo info`. The functionality of `survey`\n> > >  will be moved to another subcommand of `git repo`.\n> > > \n> > this strikes me as a bad idea, given how established the `repo` tool is.\n> \n> The `repo` tool wouldn't be executed as `git repo` though, would it? So\n> I'm not sure whether that really is relevant at all. On the other hand\n> though I do see that it might be confusing when you interact with the\n> `repo` tool on a daily basis.\n> \n> > without much thinking and reading prior conversations, i'd go with \"query\",\n> > because it's a database-like metadata ... query.\n> > the obvious followup idea would then be \"meta\", but that suggests that it\n> > isn't only a read-only command, which i think it is supposed to remain?\n> \n> \"Query\" is way too generic from my point of view, as it doesn't say\n> _what_ you query. \"Meta\" might be a bit better even though it still\n> loses the information that you act on the repository level, which is a\n> bit of a shame.\n\nAgreed, \"query\" sounds too open-ended. Since we are only targeting\nrepository level data I think it would be confusing.\n\n> We could of course adapt and call it git-repository(1) to avoid any\n> confusion with git-repo(1) and repo(1). It's not like this is a tool\n> that users would typically have to run daily outside of scripts, so I\n> don't think it hurts much to have a longer command name.\n\nBeing that git-repo(1) isn't really intended as a common user facing\ncommand, I wonder if there really would be much confusion with repo(1)\nanyway. Personally, I perfer the shorter name and think it's fine as-is,\nbut it's not a big deal either way.\n\n-Justin\n"},{"id":"522071","messageId":"aHc6y9FGFXjowkU1@pks.im","threadId":"63618","inReplyTo":"mgdervgp34m6ipfbodsfn7cztcl7gdeggzemfgivzvuyk7qtba@wdijebkuioxg","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-16T05:38:19Z","receivedAt":"2025-07-16T05:38:31Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Jul 15, 2025 at 02:15:07PM -0500, Justin Tobler wrote:\n> On 25/07/14 08:52PM, Lucas Seiki Oshiro wrote:\n> > diff --git a/builtin/repo.c b/builtin/repo.c\n> > index a1787a3cc5..dcda0d6d61 100644\n> > --- a/builtin/repo.c\n> > +++ b/builtin/repo.c\n> > @@ -1,11 +1,95 @@\n> >  #include \"builtin.h\"\n> >  #include \"parse-options.h\"\n> > +#include \"strbuf.h\"\n> > +#include \"refs.h\"\n> >  \n> > -static int repo_info(int argc UNUSED,\n> > -\t\t     const char **argv UNUSED,\n> > +typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n> > +\n> > +struct field {\n> > +\tconst char *key;\n> > +\tadd_field_fn *add_field_callback;\n> > +};\n> > +\n> > +static void add_string(struct strbuf *buf,\n> > +\t\t       const char *key, const char *value)\n> > +{\n> > +\tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n> > +}\n> \n> Any reason we add each key/value pair to a buffer instead of just\n> printing it?\n> \n> Also, as mentioned in a comment for the previous patch, maybe we should\n> support printing two output modes. For the default output, maybe a\n> simple `<key>=<value>\\n` where the any value containing special\n> characters is quoted via `quote_c_style()`.\n> \n> A null-terminated output, such as the one proposed in this patch, could\n> be enabled via a `-z` flag similar to how its done in other commands.\n\nAgreed in general, but instead of using `-z` I wonder whether it would\nmake sense to use something like `--format=key-value` and `--format=nul`\ninstead. This gives us more room to introduce additional formats in the\nfuture, like for example the JSON format that was scrapped for now.\n\nPatrick\n"},{"id":"522106","messageId":"2v7b6mpufnn6cj7u7tactgu2ibggn4xpuezl2bsfjxv62afrsa@chfrii6vkrx3","threadId":"63618","inReplyTo":"aHc6y9FGFXjowkU1@pks.im","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-16T14:04:40Z","receivedAt":"2025-07-16T14:10:24Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/16 07:38AM, Patrick Steinhardt wrote:\n> On Tue, Jul 15, 2025 at 02:15:07PM -0500, Justin Tobler wrote:\n> > Also, as mentioned in a comment for the previous patch, maybe we should\n> > support printing two output modes. For the default output, maybe a\n> > simple `<key>=<value>\\n` where the any value containing special\n> > characters is quoted via `quote_c_style()`.\n> > \n> > A null-terminated output, such as the one proposed in this patch, could\n> > be enabled via a `-z` flag similar to how its done in other commands.\n> \n> Agreed in general, but instead of using `-z` I wonder whether it would\n> make sense to use something like `--format=key-value` and `--format=nul`\n> instead. This gives us more room to introduce additional formats in the\n> future, like for example the JSON format that was scrapped for now.\n\nIf we already plan to support additional output formats, they I agree we\nshould probably a `--format=<output-type>` flag from the start. I still\nthink it would be nice to have a `-z` flag that is shorthand for\n`--format=nul` though as that is fairly common across other commands to\nhave such an option.\n\nOut of curiousity, is there a reason we are interested in supporting a\nJSON output format in addition to what is already proposed? From an\nearlier conversation I had with Lucas, it didn't seem like there was any\nparticular reason for JSON.\n\n-Justin\n"},{"id":"522126","messageId":"xmqq7c07c2fu.fsf@gitster.g","threadId":"63618","inReplyTo":"20250714235231.10137-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-16T20:20:53Z","receivedAt":"2025-07-16T20:20:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> This patch, then:\n>\n> - Renames the command to `repo` instead of `repo-info`. All the functionality\n>   of `repo-info` will now be under `repo info`. The functionality of `survey`\n>   will be moved to another subcommand of `git repo`.\n>\n> - Removes the JSON support. Given that after the previous feedback we already\n>   have a nice machine-readable format for outputting this data, JSON would not\n>   be so useful as it seemed to be at first (when the \"other format\" was just\n>   returning the values without the keys). This makes the code far more simpler,\n>   as we don't need to deal with the details of both formats.\n>\n> - Uses a simpler representation of the fields, based in their keys instead of\n>   declaring multiple enums and using nested switches. This new solution is\n>   based in a table mapping the keys and the callbacks for retrieving the data.\n>\n> - Provide a simple infrastructure for extending with the second command.\n>\n> Given that this v4 is almost a rewrite, I think it isn't worth to send a\n> range-diff.\n\nOK.  If it is almost a rewrite, then perhaps we should tart by\nmaking sure that it won't have too many irritating style gotchas to\ndiscourage reviewers from reading it ;-)\n\nThe below is from \n\n    $ git clang-format --diff $(git merge-base master HEAD) -- builtin/repo.c\n\nI removed some obviously bad suggestions but many were improvements.\n\nI'll follow this message up as if I were reviewing what clang-format\nproduced.\n\nThanks.\n\n\n builtin/repo.c | 47 ++++++++++++++++++++---------------------------\n 1 file changed, 20 insertions(+), 27 deletions(-)\n\ndiff --git c/builtin/repo.c w/builtin/repo.c\nindex d75417a48b..e8cf465da6 100644\n--- c/builtin/repo.c\n+++ w/builtin/repo.c\n@@ -14,27 +14,23 @@ struct field {\n \tadd_field_fn *add_field_callback;\n };\n \n-static void add_string(struct strbuf *buf,\n-\t\t       const char *key, const char *value)\n+static void add_string(struct strbuf *buf, const char *key, const char *value)\n {\n \tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n }\n \n-static void add_bool(struct strbuf *buf,\n-\t\t     const char *key, const int value)\n+static void add_bool(struct strbuf *buf, const char *key, const int value)\n {\n \tconst char *output_value = value ? \"true\" : \"false\";\n \tstrbuf_addf(buf, \"%s\\n%s%c\", key, output_value, '\\0');\n }\n \n-static void add_references_format(struct strbuf *buf,\n-\t\t\t\t  struct repository *repo)\n+static void add_references_format(struct strbuf *buf, struct repository *repo)\n {\n \tadd_string(buf, \"references.format\",\n \t\t   ref_storage_format_to_name(repo->ref_storage_format));\n }\n \n-\n static void add_layout_bare(struct strbuf *buf, struct repository *repo UNUSED)\n {\n \tadd_bool(buf, \"layout.bare\", is_bare_repository());\n@@ -45,11 +41,11 @@ static void add_layout_shallow(struct strbuf *buf, struct repository *repo)\n \tadd_bool(buf, \"layout.shallow\", is_repository_shallow(repo));\n }\n \n-// repo_info_fields keys should be in lexicographical order\n+/* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n-\t{\"layout.bare\", add_layout_bare},\n-\t{\"layout.shallow\", add_layout_shallow},\n-\t{\"references.format\", add_references_format},\n+\t{ \"layout.bare\", add_layout_bare },\n+\t{ \"layout.shallow\", add_layout_shallow },\n+\t{ \"references.format\", add_references_format },\n };\n \n static int repo_info_fields_cmp(const void *va, const void *vb)\n@@ -60,12 +56,13 @@ static int repo_info_fields_cmp(const void *va, const void *vb)\n \treturn strcmp(a->key, b->key);\n }\n \n-static add_field_fn *get_append_callback(const char *key) {\n-\tconst struct field search_key = {key, NULL};\n-\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n-\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n-\t\t\t\t\t    sizeof(struct field),\n-\t\t\t\t\t    repo_info_fields_cmp);\n+static add_field_fn *get_append_callback(const char *key)\n+{\n+\tconst struct field search_key = { key, NULL };\n+\tconst struct field *found =\n+\t\tbsearch(&search_key, repo_info_fields,\n+\t\t\tARRAY_SIZE(repo_info_fields), sizeof(struct field),\n+\t\t\trepo_info_fields_cmp);\n \treturn found ? found->add_field_callback : NULL;\n }\n \n@@ -77,7 +74,8 @@ static int qsort_strcmp(const void *va, const void *vb)\n \treturn strcmp(a, b);\n }\n \n-static void print_fields(int argc, const char **argv, struct repository *repo) {\n+static void print_fields(int argc, const char **argv, struct repository *repo)\n+{\n \tconst char *last = \"\";\n \tstruct strbuf buf;\n \tstrbuf_init(&buf, 256);\n@@ -91,7 +89,7 @@ static void print_fields(int argc, const char **argv, struct repository *repo) {\n \t\tif (!strcmp(key, last))\n \t\t\tcontinue;\n \n-\t\tcallback = get_append_callback(key);\n+\t\tcallback = *get_append_callback(key);\n \n \t\tif (!callback) {\n \t\t\terror(\"key %s not found\", key);\n@@ -107,19 +105,14 @@ static void print_fields(int argc, const char **argv, struct repository *repo) {\n \tstrbuf_release(&buf);\n }\n \n-static int repo_info(int argc,\n-\t\t     const char **argv,\n-\t\t     const char *prefix UNUSED,\n+static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n \t\t     struct repository *repo)\n {\n-\n-\tprint_fields(argc - 1 , argv + 1, repo);\n+\tprint_fields(argc - 1, argv + 1, repo);\n \treturn 0;\n }\n \n-int cmd_repo(int argc,\n-\t     const char **argv,\n-\t     const char *prefix,\n+int cmd_repo(int argc, const char **argv, const char *prefix,\n \t     struct repository *repo)\n {\n \tparse_opt_subcommand_fn *fn = NULL;\n"},{"id":"522127","messageId":"xmqqjz47anad.fsf@gitster.g","threadId":"63618","inReplyTo":"xmqq7c07c2fu.fsf@gitster.g","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-16T20:33:30Z","receivedAt":"2025-07-16T20:33:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> The below is from \n>\n>     $ git clang-format --diff $(git merge-base master HEAD) -- builtin/repo.c\n>\n> I removed some obviously bad suggestions but many were improvements.\n>\n> I'll follow this message up as if I were reviewing what clang-format\n> produced.\n>\n> Thanks.\n>\n>\n>  builtin/repo.c | 47 ++++++++++++++++++++---------------------------\n>  1 file changed, 20 insertions(+), 27 deletions(-)\n>\n> diff --git c/builtin/repo.c w/builtin/repo.c\n> index d75417a48b..e8cf465da6 100644\n> --- c/builtin/repo.c\n> +++ w/builtin/repo.c\n> @@ -14,27 +14,23 @@ struct field {\n>  \tadd_field_fn *add_field_callback;\n>  };\n>  \n> -static void add_string(struct strbuf *buf,\n> -\t\t       const char *key, const char *value)\n> +static void add_string(struct strbuf *buf, const char *key, const char *value)\n>  {\n>  \tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n>  }\n>  \n> -static void add_bool(struct strbuf *buf,\n> -\t\t     const char *key, const int value)\n> +static void add_bool(struct strbuf *buf, const char *key, const int value)\n>  {\n>  \tconst char *output_value = value ? \"true\" : \"false\";\n>  \tstrbuf_addf(buf, \"%s\\n%s%c\", key, output_value, '\\0');\n>  }\n>  \n> -static void add_references_format(struct strbuf *buf,\n> -\t\t\t\t  struct repository *repo)\n> +static void add_references_format(struct strbuf *buf, struct repository *repo)\n>  {\n>  \tadd_string(buf, \"references.format\",\n>  \t\t   ref_storage_format_to_name(repo->ref_storage_format));\n>  }\n\nIf the parameter list fits on a single line not just helps readers\nof a code who reads from top to bottom, but those who run \"grep\" for\nthe function name.\n\n>  \n> -\n>  static void add_layout_bare(struct strbuf *buf, struct repository *repo UNUSED)\n>  {\n>  \tadd_bool(buf, \"layout.bare\", is_bare_repository());\n\nNo reason to give double-blank between these two functions; it is\nnot like add_string/bool/references/format are closer together than\nthis add_layout_bare\n\n> @@ -45,11 +41,11 @@ static void add_layout_shallow(struct strbuf *buf, struct repository *repo)\n>  \tadd_bool(buf, \"layout.shallow\", is_repository_shallow(repo));\n>  }\n>  \n> -// repo_info_fields keys should be in lexicographical order\n> +/* repo_info_fields keys should be in lexicographical order */\n\nWe don't do // comments\n\n>  static const struct field repo_info_fields[] = {\n> -\t{\"layout.bare\", add_layout_bare},\n> -\t{\"layout.shallow\", add_layout_shallow},\n> -\t{\"references.format\", add_references_format},\n> +\t{ \"layout.bare\", add_layout_bare },\n> +\t{ \"layout.shallow\", add_layout_shallow },\n> +\t{ \"references.format\", add_references_format },\n>  };\n\nSpaces are used inside {braces} like the above.\n\n>  static int repo_info_fields_cmp(const void *va, const void *vb)\n> @@ -60,12 +56,13 @@ static int repo_info_fields_cmp(const void *va, const void *vb)\n>  \treturn strcmp(a->key, b->key);\n>  }\n>  \n> -static add_field_fn *get_append_callback(const char *key) {\n> -\tconst struct field search_key = {key, NULL};\n> -\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n> -\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n> -\t\t\t\t\t    sizeof(struct field),\n> -\t\t\t\t\t    repo_info_fields_cmp);\n> +static add_field_fn *get_append_callback(const char *key)\n> +{\n\n\"{\" opening and \"}\" closing braces around a function body sit on\ntheir own line alone without anybody else.  This is unlike the\nmulti-statement blocks in control structures (e.g. opening brace for\nthe block executed when condition holds in \"if (cond) {\" comes after\nthe closing parenthesis \")\" for the condition after a SP on the same\nline).\n\n> +\tconst struct field search_key = { key, NULL };\n\nUse of spaces inside brace pair again.\n\n> +\tconst struct field *found =\n> +\t\tbsearch(&search_key, repo_info_fields,\n> +\t\t\tARRAY_SIZE(repo_info_fields), sizeof(struct field),\n> +\t\t\trepo_info_fields_cmp);\n>  \treturn found ? found->add_field_callback : NULL;\n>  }\n>  \n> @@ -77,7 +74,8 @@ static int qsort_strcmp(const void *va, const void *vb)\n>  \treturn strcmp(a, b);\n>  }\n>  \n> -static void print_fields(int argc, const char **argv, struct repository *repo) {\n> +static void print_fields(int argc, const char **argv, struct repository *repo)\n> +{\n\nDitto.\n\n>  \tconst char *last = \"\";\n>  \tstruct strbuf buf;\n>  \tstrbuf_init(&buf, 256);\n> @@ -107,19 +105,14 @@ static void print_fields(int argc, const char **argv, struct repository *repo) {\n>  \tstrbuf_release(&buf);\n>  }\n>  \n> -static int repo_info(int argc,\n> -\t\t     const char **argv,\n> -\t\t     const char *prefix UNUSED,\n> +static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n>  \t\t     struct repository *repo)\n>  {\n> -\n> -\tprint_fields(argc - 1 , argv + 1, repo);\n\nExtra SP before \",\".\n\n> +\tprint_fields(argc - 1, argv + 1, repo);\n>  \treturn 0;\n>  }\n>  \n> -int cmd_repo(int argc,\n> -\t     const char **argv,\n> -\t     const char *prefix,\n> +int cmd_repo(int argc, const char **argv, const char *prefix,\n>  \t     struct repository *repo)\n>  {\n>  \tparse_opt_subcommand_fn *fn = NULL;\n"},{"id":"522181","messageId":"aHjPf2so4BzQkYRO@ugly","threadId":"63618","inReplyTo":"xmqqecuhiekv.fsf@gitster.g","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Oswald Buddenhagen","fromEmail":"oswald.buddenhagen@gmx.de","sentAt":"2025-07-17T10:25:03Z","receivedAt":"2025-07-17T10:25:11Z","isPatch":true,"sender":{"key":"oswald.buddenhagen@gmx.de","avatar":"https://avatars.githubusercontent.com/u/812380?v=4"},"body":"On Tue, Jul 15, 2025 at 09:49:52AM -0700, Junio C Hamano wrote:\n>Oswald Buddenhagen <oswald.buddenhagen@gmx.de> writes:\n>> the obvious followup idea would then be \"meta\", but that suggests \n>> that\n>> it isn't only a read-only command, which i think it is supposed to\n>> remain?\n>\n>\"git repository query\"\n>\"git repository stat\"\n>...?\n>\nyes, but one could also add \"git repository set\" or some such. as such \nsymmetry is reasonable to expect, people might be confused by its \nabsence. an obviously read-only command like \"query\" would preclude \nthis.\n"},{"id":"522182","messageId":"aHjTrycXSUs7XQVG@pks.im","threadId":"63618","inReplyTo":"aHjPf2so4BzQkYRO@ugly","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-17T10:42:55Z","receivedAt":"2025-07-17T10:43:04Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jul 17, 2025 at 12:25:03PM +0200, Oswald Buddenhagen wrote:\n> On Tue, Jul 15, 2025 at 09:49:52AM -0700, Junio C Hamano wrote:\n> > Oswald Buddenhagen <oswald.buddenhagen@gmx.de> writes:\n> > > the obvious followup idea would then be \"meta\", but that suggests\n> > > that\n> > > it isn't only a read-only command, which i think it is supposed to\n> > > remain?\n> > \n> > \"git repository query\"\n> > \"git repository stat\"\n> > ...?\n> > \n> yes, but one could also add \"git repository set\" or some such. as such\n> symmetry is reasonable to expect, people might be confused by its absence.\n> an obviously read-only command like \"query\" would preclude this.\n\nWell, right now it's exclusively read-only, true. But that doesn't mean\nthat there will never be a use case for adding a writing subcommand to\nit.\n\nPatrick\n"},{"id":"522205","messageId":"aHj0lF5Kbirzv3pZ@pks.im","threadId":"63618","inReplyTo":"2v7b6mpufnn6cj7u7tactgu2ibggn4xpuezl2bsfjxv62afrsa@chfrii6vkrx3","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-17T13:03:16Z","receivedAt":"2025-07-17T13:03:23Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Jul 16, 2025 at 09:04:40AM -0500, Justin Tobler wrote:\n> On 25/07/16 07:38AM, Patrick Steinhardt wrote:\n> > On Tue, Jul 15, 2025 at 02:15:07PM -0500, Justin Tobler wrote:\n> > > Also, as mentioned in a comment for the previous patch, maybe we should\n> > > support printing two output modes. For the default output, maybe a\n> > > simple `<key>=<value>\\n` where the any value containing special\n> > > characters is quoted via `quote_c_style()`.\n> > > \n> > > A null-terminated output, such as the one proposed in this patch, could\n> > > be enabled via a `-z` flag similar to how its done in other commands.\n> > \n> > Agreed in general, but instead of using `-z` I wonder whether it would\n> > make sense to use something like `--format=key-value` and `--format=nul`\n> > instead. This gives us more room to introduce additional formats in the\n> > future, like for example the JSON format that was scrapped for now.\n> \n> If we already plan to support additional output formats, they I agree we\n> should probably a `--format=<output-type>` flag from the start. I still\n> think it would be nice to have a `-z` flag that is shorthand for\n> `--format=nul` though as that is fairly common across other commands to\n> have such an option.\n> \n> Out of curiousity, is there a reason we are interested in supporting a\n> JSON output format in addition to what is already proposed? From an\n> earlier conversation I had with Lucas, it didn't seem like there was any\n> particular reason for JSON.\n\nI don't care much about the JSON output format. What I do care about is\nto have a default format that we are free to iterate on, especially in\nthe context of `git repo size`. If we ever want to have output that is\nas user friendly as git-sizer(1)'s we need to have that freedom.\n\nFrom my perspective that necessitates two things:\n\n  - That we are able to change formats with a proper `--format=` flag.\n    `-z` alone doesn't fit that bill, as we are already talking about\n    there formats: user friendly, key-value pairs, and key-value pairs\n    with NUL termination.\n\n    That being said I'm not opposed to also have `-z` as an alias as\n    long as we also have `--format=`.\n\n  - That the _default_ format is the user friendly format that we can\n    iterate on, at least for `git repo size`. Otherwise we have already\n    failed on our mission to supply a user-friendly alternative to\n    git-sizer(1).\n\nPatrick\n"},{"id":"522211","messageId":"oir6mpubnuplnjatdhlyljslakmh72wlax4afuqnsiyob65xfh@5rft32ln2zvk","threadId":"63618","inReplyTo":"aHj0lF5Kbirzv3pZ@pks.im","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-17T16:06:14Z","receivedAt":"2025-07-17T16:11:59Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/17 03:03PM, Patrick Steinhardt wrote:\n> On Wed, Jul 16, 2025 at 09:04:40AM -0500, Justin Tobler wrote:\n> > If we already plan to support additional output formats, they I agree we\n> > should probably a `--format=<output-type>` flag from the start. I still\n> > think it would be nice to have a `-z` flag that is shorthand for\n> > `--format=nul` though as that is fairly common across other commands to\n> > have such an option.\n> > \n> > Out of curiousity, is there a reason we are interested in supporting a\n> > JSON output format in addition to what is already proposed? From an\n> > earlier conversation I had with Lucas, it didn't seem like there was any\n> > particular reason for JSON.\n> \n> I don't care much about the JSON output format. What I do care about is\n> to have a default format that we are free to iterate on, especially in\n> the context of `git repo size`. If we ever want to have output that is\n> as user friendly as git-sizer(1)'s we need to have that freedom.\n> \n> From my perspective that necessitates two things:\n> \n>   - That we are able to change formats with a proper `--format=` flag.\n>     `-z` alone doesn't fit that bill, as we are already talking about\n>     there formats: user friendly, key-value pairs, and key-value pairs\n>     with NUL termination.\n> \n>     That being said I'm not opposed to also have `-z` as an alias as\n>     long as we also have `--format=`.\n> \n>   - That the _default_ format is the user friendly format that we can\n>     iterate on, at least for `git repo size`. Otherwise we have already\n>     failed on our mission to supply a user-friendly alternative to\n>     git-sizer(1).\n\nI was originally of the mindset that git-repo(1) would focus on being\nmore of a plumbing commmand and if we wanted to provide a more\nuser-friendly decorated format for something akin to git-sizer(1) info,\nthat would be better done through a separate command.\n\nThinking about this some more though, I agree with you that we should\nprobably just have git-repo(1) be the single stop and provide the\nvarious output flavors for its subcommands. If this is the route we go\ndown, the default mode should be user-friendly.\n\nI think a key attribute of this user-friendly mode is that it should be\nfree to change and be iterated on. If a user wants a stable output\nformat, they should request the key-value output form that is\nline/nul-delimited.\n\nSo maybe we have three different output modes for `--format=<mode>`:\n`user` (default), `keyvalue`, and `nul`? For `git repo info`, I'm not\nentirely certain how the default user-friendly mode would/should differ\nfrom the key-value one, but maybe that is not something we need to worry\nabout right now. For now, the two modes could match and over time update\nthe user mode as desired.\n\n-Justin\n"},{"id":"522287","messageId":"A5D456FD-C8C3-4087-8C08-E931BD254390@gmail.com","threadId":"63618","inReplyTo":"aHZCipKCPIu8mPly@pks.im","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-18T19:13:29Z","receivedAt":"2025-07-18T19:13:44Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Is there any reason why the callback appends to a buffer instead of\n> printing the data immediately?\n\nEven though I answered you about that in our meeting, I'll answer you\nagain only to make it transparent for the other reviewers.\n\nMy idea was to build the output string and if something goes wrong\n(e.g. the user requested a invalid key after some valid keys), the\nprocess would exit without any output.\n\nAfter our meeting, we agreed that this wouldn't be so important and it\nwould be better making the code simpler by just printing the key-value\npairs.\n"},{"id":"522288","messageId":"6ED935E8-05CE-4155-B56C-A113926915E6@gmail.com","threadId":"63618","inReplyTo":"oir6mpubnuplnjatdhlyljslakmh72wlax4afuqnsiyob65xfh@5rft32ln2zvk","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-18T20:26:48Z","receivedAt":"2025-07-18T20:27:04Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> So maybe we have three different output modes for `--format=<mode>`:\n> `user` (default), `keyvalue`, and `nul`?\n\nWhat about `porcelain` (default), `keyvalue` and `null` (being `-z` an\nalias for this last one)?\n\n> For `git repo info`, I'm not entirely certain how the default\n> user-friendly mode would/should differ from the key-value one\n\nEven for the \"survey\" part, would it be useful to have a key-value and\nanother human-readable format?\n\nAnother thing that I was thinking (as a future feature): what about\nusing by default the user-friendly format and using the null-terminated\nformat when piping the output to another command (like git-diff does\nwith the colors)?\n\n"},{"id":"522342","messageId":"34671B47-21FF-464E-A811-32759D077A96@gmail.com","threadId":"63618","inReplyTo":"aHZChXPTbk_X6xDK@pks.im","subject":"Re: [GSoC RFC PATCH v4 1/4] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-20T19:51:49Z","receivedAt":"2025-07-20T19:52:05Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n>> + argc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n>> +\n>> + if (fn) {\n>> + return fn(argc, argv, prefix, repo);\n>> + } else {\n>> + if (argc) {\n>> + error(_(\"unknown subcommand: `%s'\"), argv[0]);\n>> + usage_with_options(repo_usage, options);\n>> + }\n>> + return 1;\n> \n> I think we need to print an error `if (!argc)`, as well. Otherwise the\n> user wouldn't know why `git repo` without any argumentsdoesn't do\n> anything.\n\nActually, it already does it! The subcommand parsing introduced in fa83cc834d\n(parse-options: add support for parsing subcommands, 2022-08-19) displays\n\"error: need a subcommand\" and the command usage when no subcommand is provided.\n\nActually, this whole if-else can be removed. When an invalid command is\nprovided, the execution is aborted in parse_options, so it's safe to assume that\nfn is a valid function.\n\nI'll change that."},{"id":"522360","messageId":"z3lvhetfvxi5wylqielzydjkqyokwtzgsmwbwiq4xx5nfta6xw@2wfnmp2pzvcu","threadId":"63618","inReplyTo":"6ED935E8-05CE-4155-B56C-A113926915E6@gmail.com","subject":"Re: [GSoC RFC PATCH v4 2/4] repo: add the field references.format","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-21T14:41:31Z","receivedAt":"2025-07-21T14:47:20Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/18 05:26PM, Lucas Seiki Oshiro wrote:\n> \n> > So maybe we have three different output modes for `--format=<mode>`:\n> > `user` (default), `keyvalue`, and `nul`?\n> \n> What about `porcelain` (default), `keyvalue` and `null` (being `-z` an\n> alias for this last one)?\n\nSure that also would work.\n\n> \n> > For `git repo info`, I'm not entirely certain how the default\n> > user-friendly mode would/should differ from the key-value one\n> \n> Even for the \"survey\" part, would it be useful to have a key-value and\n> another human-readable format?\n\nThe only use I would see if it a user would want a non-decorated output\nthat is still user-friendly (ie not nul delimited). There is also no\nreason we couldn't add this later in the future if we need it. For now,\nwe could just focus on the \"porcelain\" and \"nul\" formats if we want.\n\n> Another thing that I was thinking (as a future feature): what about\n> using by default the user-friendly format and using the null-terminated\n> format when piping the output to another command (like git-diff does\n> with the colors)?\n\nI don't have a strong opinion, but I'm somewhat of the mindset that the\noutput format should be an explicit choice. Maybe the user would want to\nmanipulate the user-friendly ouput is some way.\n\n-Justin\n"},{"id":"522375","messageId":"B8F0E5C1-4664-4A95-AE5B-0C692174222B@gmail.com","threadId":"63618","inReplyTo":"xmqq7c07c2fu.fsf@gitster.g","subject":"Re: [GSoC RFC PATCH v4 0/4] repo: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-21T22:05:46Z","receivedAt":"2025-07-21T22:06:01Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n>    $ git clang-format --diff $(git merge-base master HEAD) -- builtin/repo.c\n\nThanks for your suggestion! I didn't know about it, I'll use to check my\nnext patches."},{"id":"522381","messageId":"20250722002835.33428-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 0/5] repo: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-22T00:28:30Z","receivedAt":"2025-07-22T00:29:12Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\nThis fifth version addresses the issues from v4.\n\nThe major changes are:\n\n- A new output format was added: key=value<LF>. This is the default format.\n\n- The values are printed right after their retrieval, this way, I dropped the\n  use of strbuf.\n\n- The callbacks now return strings instead of filling the strbuf.\n\nThings that didn't change:\n\n- After the previous discussions (and the internal discussion with my mentors),\n  I'll still keep the name `git repo`.\n\nFuture work:\n\n- The current callback format works for the current values, but won't work for\n  other fields that may require dynamically allocated strings (e.g. numbers and\n  paths). I'm thinking about changing the callbacks to return strbufs instead\n  of strings, but I'm open for suggestions.\n\n- Add `-z` and/or `--null` as alias for `--format=null`.\n\nHere's the rangediff from v4 to v5:\n\n1:  c5f08d4342 ! 1:  396cfc256a repo: declare the repo command\n    @@ Commit message\n         repo: declare the repo command\n\n         Currently, `git rev-parse` covers a wide range of functionality not\n    -    directly related to parsing revisions, as its name says. Over time,\n    +    directly related to parsing revisions, as its name suggests. Over time,\n         many features like parsing datestrings, options, paths, and others\n    -    were added to it because there wasn't a more appropriated command\n    +    were added to it because there wasn't a more appropriate command\n         to place them.\n\n         Create a new Git command called `repo`. `git repo` will be the main\n    @@ Commit message\n         will bring the functionality of retrieving repository-related\n         information currently returned by `rev-parse`.\n\n    -    Also add entries for this new command in:\n    -\n    -    - the build files (Makefile, meson.build, Documentation/meson.build)\n    -    - builtin.h\n    -    - git.c\n    -    - .gitignore\n    -    - command-list.txt\n    -    - Documentation\n    +    Add the required tests, documentation and build changes to enable\n    +    usage of this subcommand.\n\n         Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Helped-by: Junio C Hamano <gitster@pobox.com>\n    @@ Documentation/git-repo.adoc (new)\n     +\n     +DESCRIPTION\n     +-----------\n    -+Retrieve information about the current repository in a machine-readable format.\n    -+\n    -+`git repo` will be the primary tool to query repository-specific information,\n    -+such as metadata that currently can also be done by calling `git rev-parse` (see\n    -+linkgit:git-rev-parse[1]). `git repo` doesn't query information unrelated to the\n    -+current repository or that is already retrieved by a specialized command, for\n    -+example, `git config` (see linkgit:git-config[1]) or `git var` (see\n    -+linkgit:git-var[1]).\n    ++This command retrieve repository level information.\n     +\n    -+This command returns the retrieved data following a null-terminated format with\n    -+this syntax:\n    -++\n    -+----------------\n    -+key1<LF>value1<NUL>\n    -+key2<LF>value2<NUL>\n    -+...\n    -+----------------\n    -++\n     +THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n     +\n     +COMMANDS\n    @@ builtin/repo.c (new)\n     +#include \"builtin.h\"\n     +#include \"parse-options.h\"\n     +\n    -+static int repo_info(int argc UNUSED,\n    -+\t\t     const char **argv UNUSED,\n    -+\t\t     const char *prefix UNUSED,\n    -+\t\t     struct repository *repo UNUSED)\n    ++static int repo_info(int argc UNUSED, const char **argv UNUSED,\n    ++\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n     +{\n     +\treturn 0;\n     +}\n     +\n    -+int cmd_repo(int argc,\n    -+\t     const char **argv,\n    -+\t     const char *prefix,\n    ++int cmd_repo(int argc, const char **argv, const char *prefix,\n     +\t     struct repository *repo)\n     +{\n     +\tparse_opt_subcommand_fn *fn = NULL;\n     +\tconst char *const repo_usage[] = {\n    -+\t\t\"git repo info\",\n    ++\t\t\"git repo info [<key>...]\",\n     +\t\tNULL\n     +\t};\n     +\tstruct option options[] = {\n    @@ builtin/repo.c (new)\n     +\n     +\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n     +\n    -+\tif (fn) {\n    -+\t\treturn fn(argc, argv, prefix, repo);\n    -+\t} else {\n    -+\t\tif (argc) {\n    -+\t\t\terror(_(\"unknown subcommand: `%s'\"), argv[0]);\n    -+\t\t\tusage_with_options(repo_usage, options);\n    -+\t\t}\n    -+\t\treturn 1;\n    -+\t}\n    ++\treturn fn(argc, argv, prefix, repo);\n     +}\n\n      ## command-list.txt ##\n2:  60494d5a17 ! 2:  28f4c21a96 repo: add the field references.format\n    @@ Metadata\n      ## Commit message ##\n         repo: add the field references.format\n\n    -    This commit is part of the series that introduce the new subcommand\n    +    This commit is part of the series that introduces the new subcommand\n         git-repo-info.\n\n         The flag `--show-ref-format` from git-rev-parse is used for retrieving\n    @@ Documentation/git-repo.adoc: INFO KEYS\n\n     +`references`::\n     +Reference-related data:\n    -+* `format`: the reference storage format, either `files` or `reftable`.\n    ++* `format`: the reference storage format\n     +\n      SEE ALSO\n      --------\n    @@ builtin/repo.c\n     @@\n      #include \"builtin.h\"\n      #include \"parse-options.h\"\n    -+#include \"strbuf.h\"\n     +#include \"refs.h\"\n\n    --static int repo_info(int argc UNUSED,\n    --\t\t     const char **argv UNUSED,\n    -+typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n    +-static int repo_info(int argc UNUSED, const char **argv UNUSED,\n    +-\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n    ++typedef const char *get_value_fn(struct repository *repo);\n     +\n     +struct field {\n     +\tconst char *key;\n    -+\tadd_field_fn *add_field_callback;\n    ++\tget_value_fn *add_field_callback;\n     +};\n     +\n    -+static void add_string(struct strbuf *buf,\n    -+\t\t       const char *key, const char *value)\n    ++static const char *get_references_format(struct repository *repo)\n     +{\n    -+\tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n    ++\treturn ref_storage_format_to_name(repo->ref_storage_format);\n     +}\n     +\n    -+static void add_references_format(struct strbuf *buf,\n    -+\t\t\t\t  struct repository *repo)\n    -+{\n    -+\tadd_string(buf, \"references.format\",\n    -+\t\t   ref_storage_format_to_name(repo->ref_storage_format));\n    -+}\n    -+\n    -+// repo_info_fields keys should be in lexicographical order\n    ++/* repo_info_fields keys should be in lexicographical order */\n     +static const struct field repo_info_fields[] = {\n    -+\t{\"references.format\", add_references_format},\n    ++\t{ \"references.format\", get_references_format },\n     +};\n     +\n     +static int repo_info_fields_cmp(const void *va, const void *vb)\n    @@ builtin/repo.c\n     +\treturn strcmp(a->key, b->key);\n     +}\n     +\n    -+static add_field_fn *get_append_callback(const char *key) {\n    -+\tconst struct field search_key = {key, NULL};\n    ++static get_value_fn *get_value_callback(const char *key)\n    + {\n    ++\tconst struct field search_key = { key, NULL };\n     +\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n     +\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n     +\t\t\t\t\t    sizeof(struct field),\n    @@ builtin/repo.c\n     +\treturn strcmp(a, b);\n     +}\n     +\n    -+static void print_fields(int argc, const char **argv, struct repository *repo) {\n    ++static int print_fields(int argc, const char **argv, struct repository *repo)\n    ++{\n     +\tconst char *last = \"\";\n    -+\tstruct strbuf buf;\n    -+\tstrbuf_init(&buf, 256);\n     +\n     +\tQSORT(argv, argc, qsort_strcmp);\n     +\n     +\tfor (int i = 0; i < argc; i++) {\n    -+\t\tadd_field_fn *callback;\n    ++\t\tget_value_fn *callback;\n     +\t\tconst char *key = argv[i];\n    ++\t\tconst char *value;\n     +\n     +\t\tif (!strcmp(key, last))\n     +\t\t\tcontinue;\n     +\n    -+\t\tcallback = get_append_callback(key);\n    ++\t\tcallback = get_value_callback(key);\n     +\n    -+\t\tif (!callback) {\n    -+\t\t\terror(\"key %s not found\", key);\n    -+\t\t\tstrbuf_release(&buf);\n    -+\t\t\texit(1);\n    -+\t\t}\n    ++\t\tif (!callback)\n    ++\t\t\treturn error(\"key %s not found\", key);\n     +\n    -+\t\tcallback(&buf, repo);\n    ++\t\tvalue = callback(repo);\n    ++\t\tprintf(\"%s=%s\\n\", key, value);\n     +\t\tlast = key;\n     +\t}\n     +\n    -+\tfwrite(buf.buf, 1, buf.len, stdout);\n    -+\tstrbuf_release(&buf);\n    -+}\n    -+\n    -+static int repo_info(int argc,\n    -+\t\t     const char **argv,\n    - \t\t     const char *prefix UNUSED,\n    --\t\t     struct repository *repo UNUSED)\n    -+\t\t     struct repository *repo)\n    - {\n    -+\n    -+\tprint_fields(argc - 1 , argv + 1, repo);\n      \treturn 0;\n      }\n\n    -@@ builtin/repo.c: int cmd_repo(int argc,\n    ++static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n    ++\t\t     struct repository *repo)\n    ++{\n    ++\treturn print_fields(argc - 1, argv + 1, repo);\n    ++}\n    ++\n    + int cmd_repo(int argc, const char **argv, const char *prefix,\n    + \t     struct repository *repo)\n      {\n    - \tparse_opt_subcommand_fn *fn = NULL;\n    - \tconst char *const repo_usage[] = {\n    --\t\t\"git repo info\",\n    -+\t\t\"git repo info [<key>...]\",\n    - \t\tNULL\n    - \t};\n    - \tstruct option options[] = {\n\n      ## t/meson.build ##\n     @@ t/meson.build: integration_tests = [\n    @@ t/t1900-repo.sh (new)\n     +\ttest_expect_success \"$label\" '\n     +\t\ttest_when_finished \"rm -rf repo\" &&\n     +\t\teval \"$init_command\" &&\n    -+\t\techo \"$expected_value\" | lf_to_nul >expected &&\n    ++\t\techo \"$expected_value\" >expected &&\n     +\t\tgit -C repo repo info \"$key\" >output &&\n    -+\t\ttail -n 1 output >actual &&\n    ++\t\tcut -d \"=\" -f 2 <output >actual &&\n     +\t\ttest_cmp expected actual\n     +\t'\n     +}\n    @@ t/t1900-repo.sh (new)\n     +\tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n     +\n     +test_expect_success \"only one value is returned if the same key is requested twice\" '\n    -+\techo \"references.format\" > expected &&\n    -+\tgit rev-parse --show-ref-format > ref-format &&\n    -+\tlf_to_nul <ref-format >>expected &&\n    -+\tgit repo info references.format references.format > actual &&\n    -+\ttest_cmp expected actual\n    ++\ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n    ++\techo \"references.format\" >expected_key &&\n    ++\tgit rev-parse --show-ref-format >expected_value &&\n    ++\tgit repo info references.format references.format >output &&\n    ++\tcut -d \"=\" -f 1 <output >actual_key &&\n    ++\tcut -d \"=\" -f 2 <output >actual_value &&\n    ++        test_cmp expected_key actual_key &&\n    ++        test_cmp expected_value actual_value\n     +'\n     +\n     +test_done\n3:  f0a318c3a7 ! 3:  b8001ae87e repo: add field layout.bare\n    @@ Commit message\n      ## Documentation/git-repo.adoc ##\n     @@ Documentation/git-repo.adoc: categories:\n      Reference-related data:\n    - * `format`: the reference storage format, either `files` or `reftable`.\n    + * `format`: the reference storage format\n\n     +`layout`::\n     +Information about the how the current repository is represented:\n    @@ builtin/repo.c\n     +\n      #include \"builtin.h\"\n      #include \"parse-options.h\"\n    - #include \"strbuf.h\"\n      #include \"refs.h\"\n     +#include \"environment.h\"\n\n    - typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n    + typedef const char *get_value_fn(struct repository *repo);\n\n    -@@ builtin/repo.c: static void add_string(struct strbuf *buf,\n    - \tstrbuf_addf(buf, \"%s\\n%s%c\", key, value, '\\0');\n    - }\n    +@@ builtin/repo.c: struct field {\n    + \tget_value_fn *add_field_callback;\n    + };\n\n    -+static void add_bool(struct strbuf *buf,\n    -+\t\t     const char *key, const int value)\n    ++static const char *get_layout_bare(struct repository *repo UNUSED)\n     +{\n    -+\tconst char *output_value = value ? \"true\" : \"false\";\n    -+\tstrbuf_addf(buf, \"%s\\n%s%c\", key, output_value, '\\0');\n    ++\treturn is_bare_repository() ? \"true\" : \"false\";\n     +}\n     +\n    - static void add_references_format(struct strbuf *buf,\n    - \t\t\t\t  struct repository *repo)\n    + static const char *get_references_format(struct repository *repo)\n      {\n    -@@ builtin/repo.c: static void add_references_format(struct strbuf *buf,\n    - \t\t   ref_storage_format_to_name(repo->ref_storage_format));\n    - }\n    + \treturn ref_storage_format_to_name(repo->ref_storage_format);\n    +@@ builtin/repo.c: static const char *get_references_format(struct repository *repo)\n\n    -+\n    -+static void add_layout_bare(struct strbuf *buf, struct repository *repo UNUSED)\n    -+{\n    -+\tadd_bool(buf, \"layout.bare\", is_bare_repository());\n    -+}\n    -+\n    - // repo_info_fields keys should be in lexicographical order\n    + /* repo_info_fields keys should be in lexicographical order */\n      static const struct field repo_info_fields[] = {\n    -+\t{\"layout.bare\", add_layout_bare},\n    - \t{\"references.format\", add_references_format},\n    ++\t{ \"layout.bare\", get_layout_bare },\n    + \t{ \"references.format\", get_references_format },\n      };\n\n\n    @@ t/t1900-repo.sh: test_repo_info 'ref format files is retrieved correctly' '\n     +\tgit init --bare repo' 'layout.bare' 'true'\n     +\n      test_expect_success \"only one value is returned if the same key is requested twice\" '\n    - \techo \"references.format\" > expected &&\n    - \tgit rev-parse --show-ref-format > ref-format &&\n    + \ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n    + \techo \"references.format\" >expected_key &&\n4:  220a3e6ca7 ! 4:  bceba54e8b repo: add field layout.shallow\n    @@ Documentation/git-repo.adoc: Reference-related data:\n\n      ## builtin/repo.c ##\n     @@\n    - #include \"strbuf.h\"\n    + #include \"parse-options.h\"\n      #include \"refs.h\"\n      #include \"environment.h\"\n     +#include \"shallow.h\"\n\n    - typedef void add_field_fn(struct strbuf *buf, struct repository *repo);\n    + typedef const char *get_value_fn(struct repository *repo);\n\n    -@@ builtin/repo.c: static void add_layout_bare(struct strbuf *buf, struct repository *repo UNUSED)\n    - \tadd_bool(buf, \"layout.bare\", is_bare_repository());\n    +@@ builtin/repo.c: static const char *get_layout_bare(struct repository *repo UNUSED)\n    + \treturn is_bare_repository() ? \"true\" : \"false\";\n      }\n\n    -+static void add_layout_shallow(struct strbuf *buf, struct repository *repo)\n    ++static const char *get_layout_shallow(struct repository *repo)\n     +{\n    -+\tadd_bool(buf, \"layout.shallow\", is_repository_shallow(repo));\n    ++\treturn is_repository_shallow(repo) ? \"true\" : \"false\";\n     +}\n     +\n    - // repo_info_fields keys should be in lexicographical order\n    + static const char *get_references_format(struct repository *repo)\n    + {\n    + \treturn ref_storage_format_to_name(repo->ref_storage_format);\n    +@@ builtin/repo.c: static const char *get_references_format(struct repository *repo)\n    + /* repo_info_fields keys should be in lexicographical order */\n      static const struct field repo_info_fields[] = {\n    - \t{\"layout.bare\", add_layout_bare},\n    -+\t{\"layout.shallow\", add_layout_shallow},\n    - \t{\"references.format\", add_references_format},\n    + \t{ \"layout.bare\", get_layout_bare },\n    ++\t{ \"layout.shallow\", get_layout_shallow },\n    + \t{ \"references.format\", get_references_format },\n      };\n\n\n    @@ t/t1900-repo.sh: test_repo_info 'bare repository = false is retrieved correctly'\n     +\t' 'layout.shallow' 'true'\n     +\n      test_expect_success \"only one value is returned if the same key is requested twice\" '\n    - \techo \"references.format\" > expected &&\n    - \tgit rev-parse --show-ref-format > ref-format &&\n    + \ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n    + \techo \"references.format\" >expected_key &&\n     @@ t/t1900-repo.sh: test_expect_success \"only one value is returned if the same key is requested twi\n    - \ttest_cmp expected actual\n    +         test_cmp expected_value actual_value\n      '\n\n     +test_expect_success 'output is returned correctly when two keys are requested' '\n     +\ttest_when_finished \"rm -f expect\" &&\n    -+\tprintf \"layout.bare\\nfalseQlayout.shallow\\nfalseQ\" >expect &&\n    -+\tgit repo info layout.shallow layout.bare >output &&\n    -+\tnul_to_q <output >actual &&\n    ++\tprintf \"layout.bare=false\\nlayout.shallow=false\\n\" >expect &&\n    ++\tgit repo info layout.shallow layout.bare >actual &&\n     +\ttest_cmp expect actual\n     +'\n     +\n-:  ---------- > 5:  f4a2b0a04e repo: add the --format flag\n\nLucas Seiki Oshiro (5):\n  repo: declare the repo command\n  repo: add the field references.format\n  repo: add field layout.bare\n  repo: add field layout.shallow\n  repo: add the --format flag\n\n .gitignore                  |   1 +\n Documentation/git-repo.adoc |  58 ++++++++++++++\n Documentation/meson.build   |   1 +\n Makefile                    |   1 +\n builtin.h                   |   1 +\n builtin/repo.c              | 154 ++++++++++++++++++++++++++++++++++++\n command-list.txt            |   1 +\n git.c                       |   1 +\n meson.build                 |   1 +\n t/meson.build               |   1 +\n t/t1900-repo.sh             |  86 ++++++++++++++++++++\n 11 files changed, 306 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n create mode 100755 t/t1900-repo.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522382","messageId":"20250722002835.33428-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250722002835.33428-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-22T00:28:31Z","receivedAt":"2025-07-22T00:29:15Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, `git rev-parse` covers a wide range of functionality not\ndirectly related to parsing revisions, as its name suggests. Over time,\nmany features like parsing datestrings, options, paths, and others\nwere added to it because there wasn't a more appropriate command\nto place them.\n\nCreate a new Git command called `repo`. `git repo` will be the main\ncommand for obtaining the information about a repository (such as\nmetadata and metrics), returning them in a machine readable format\nfollowing the syntax \"field<LF>value<NUL>\".\n\nAlso declare a subcommand for `repo` called `info`. `git repo info`\nwill bring the functionality of retrieving repository-related\ninformation currently returned by `rev-parse`.\n\nAdd the required tests, documentation and build changes to enable\nusage of this subcommand.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore                  |  1 +\n Documentation/git-repo.adoc | 38 +++++++++++++++++++++++++++++++++++++\n Documentation/meson.build   |  1 +\n Makefile                    |  1 +\n builtin.h                   |  1 +\n builtin/repo.c              | 26 +++++++++++++++++++++++++\n command-list.txt            |  1 +\n git.c                       |  1 +\n meson.build                 |  1 +\n 9 files changed, 71 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..1803023427 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nnew file mode 100644\nindex 0000000000..caee7d8aef\n--- /dev/null\n+++ b/Documentation/git-repo.adoc\n@@ -0,0 +1,38 @@\n+git-repo(1)\n+===========\n+\n+NAME\n+----\n+git-repo - Retrieve information about a repository\n+\n+SYNOPSIS\n+--------\n+[synopsis]\n+git repo info [<key>...]\n+\n+DESCRIPTION\n+-----------\n+This command retrieve repository level information.\n+\n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n+COMMANDS\n+--------\n+info [<key>...]::\n+\tRetrieve metadata-related information about the current repository. Only\n+\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n+\tsection below).\n+\n+INFO KEYS\n+---------\n+\n+The set of data that `git repo` can return is grouped into the following\n+categories:\n+\n+SEE ALSO\n+--------\n+linkgit:git-rev-parse[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 2fe1a1369d..1ebdd57789 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -116,6 +116,7 @@ manpages = {\n   'git-repack.adoc' : 1,\n   'git-replace.adoc' : 1,\n   'git-replay.adoc' : 1,\n+  'git-repo.adoc' : 1,\n   'git-request-pull.adoc' : 1,\n   'git-rerere.adoc' : 1,\n   'git-reset.adoc' : 1,\ndiff --git a/Makefile b/Makefile\nindex 5f7dd79dfa..9dce446309 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1306,6 +1306,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..e6458e6fb9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo.c b/builtin/repo.c\nnew file mode 100644\nindex 0000000000..d4f01e35e2\n--- /dev/null\n+++ b/builtin/repo.c\n@@ -0,0 +1,26 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+static int repo_info(int argc UNUSED, const char **argv UNUSED,\n+\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+{\n+\treturn 0;\n+}\n+\n+int cmd_repo(int argc, const char **argv, const char *prefix,\n+\t     struct repository *repo)\n+{\n+\tparse_opt_subcommand_fn *fn = NULL;\n+\tconst char *const repo_usage[] = {\n+\t\t\"git repo info [<key>...]\",\n+\t\tNULL\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\treturn fn(argc, argv, prefix, repo);\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex b7ade3ab9f..1b0bdee00d 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -164,6 +164,7 @@ git-remote                              ancillarymanipulators           complete\n git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-replay                              plumbingmanipulators\n+git-repo                                plumbinginterrogators\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           history\ndiff --git a/git.c b/git.c\nindex 07a5fe39fb..8290d8b8c8 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo\", cmd_repo, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 9579377f3d..15d3e3701f 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522383","messageId":"20250722002835.33428-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250722002835.33428-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-22T00:28:32Z","receivedAt":"2025-07-22T00:29:18Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--show-ref-format` from git-rev-parse is used for retrieving\nthe reference format (i.e. `files` or `reftable`). This way, it is\nused for querying repository metadata, fitting in the purpose of\ngit-repo-info.\n\nThen, add a new field `references.format` to the repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  4 ++\n builtin/repo.c              | 75 ++++++++++++++++++++++++++++++++++++-\n t/meson.build               |  1 +\n t/t1900-repo.sh             | 50 +++++++++++++++++++++++++\n 4 files changed, 128 insertions(+), 2 deletions(-)\n create mode 100755 t/t1900-repo.sh\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex caee7d8aef..cf8483ec49 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -29,6 +29,10 @@ INFO KEYS\n The set of data that `git repo` can return is grouped into the following\n categories:\n \n+`references`::\n+Reference-related data:\n+* `format`: the reference storage format\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex d4f01e35e2..5beae0f781 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,12 +1,83 @@\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"refs.h\"\n \n-static int repo_info(int argc UNUSED, const char **argv UNUSED,\n-\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+typedef const char *get_value_fn(struct repository *repo);\n+\n+struct field {\n+\tconst char *key;\n+\tget_value_fn *add_field_callback;\n+};\n+\n+static const char *get_references_format(struct repository *repo)\n+{\n+\treturn ref_storage_format_to_name(repo->ref_storage_format);\n+}\n+\n+/* repo_info_fields keys should be in lexicographical order */\n+static const struct field repo_info_fields[] = {\n+\t{ \"references.format\", get_references_format },\n+};\n+\n+static int repo_info_fields_cmp(const void *va, const void *vb)\n+{\n+\tconst struct field *a = va;\n+\tconst struct field *b = vb;\n+\n+\treturn strcmp(a->key, b->key);\n+}\n+\n+static get_value_fn *get_value_callback(const char *key)\n {\n+\tconst struct field search_key = { key, NULL };\n+\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n+\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n+\t\t\t\t\t    sizeof(struct field),\n+\t\t\t\t\t    repo_info_fields_cmp);\n+\treturn found ? found->add_field_callback : NULL;\n+}\n+\n+static int qsort_strcmp(const void *va, const void *vb)\n+{\n+\tconst char *a = *(const char **)va;\n+\tconst char *b = *(const char **)vb;\n+\n+\treturn strcmp(a, b);\n+}\n+\n+static int print_fields(int argc, const char **argv, struct repository *repo)\n+{\n+\tconst char *last = \"\";\n+\n+\tQSORT(argv, argc, qsort_strcmp);\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tget_value_fn *callback;\n+\t\tconst char *key = argv[i];\n+\t\tconst char *value;\n+\n+\t\tif (!strcmp(key, last))\n+\t\t\tcontinue;\n+\n+\t\tcallback = get_value_callback(key);\n+\n+\t\tif (!callback)\n+\t\t\treturn error(\"key %s not found\", key);\n+\n+\t\tvalue = callback(repo);\n+\t\tprintf(\"%s=%s\\n\", key, value);\n+\t\tlast = key;\n+\t}\n+\n \treturn 0;\n }\n \n+static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+\t\t     struct repository *repo)\n+{\n+\treturn print_fields(argc - 1, argv + 1, repo);\n+}\n+\n int cmd_repo(int argc, const char **argv, const char *prefix,\n \t     struct repository *repo)\n {\ndiff --git a/t/meson.build b/t/meson.build\nindex 1af289425d..8693e6abc4 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -245,6 +245,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nnew file mode 100755\nindex 0000000000..f072c7c67b\n--- /dev/null\n+++ b/t/t1900-repo.sh\n@@ -0,0 +1,50 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+\n+. ./test-lib.sh\n+\n+# Test if a field is correctly returned in the null-terminated format\n+#\n+# Usage: test_repo_info <label> <init command> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init command: a command that creates a repository called 'repo', configured\n+#      accordingly to what is being tested\n+#   key: the key of the field that is being tested\n+#   expected value: the value that the field should contain\n+test_repo_info () {\n+\tlabel=$1\n+\tinit_command=$2\n+\tkey=$3\n+\texpected_value=$4\n+\n+\ttest_expect_success \"$label\" '\n+\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\teval \"$init_command\" &&\n+\t\techo \"$expected_value\" >expected &&\n+\t\tgit -C repo repo info \"$key\" >output &&\n+\t\tcut -d \"=\" -f 2 <output >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n+\n+test_repo_info 'ref format files is retrieved correctly' '\n+\tgit init --ref-format=files repo' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' '\n+\tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n+\n+test_expect_success \"only one value is returned if the same key is requested twice\" '\n+\ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n+\techo \"references.format\" >expected_key &&\n+\tgit rev-parse --show-ref-format >expected_value &&\n+\tgit repo info references.format references.format >output &&\n+\tcut -d \"=\" -f 1 <output >actual_key &&\n+\tcut -d \"=\" -f 2 <output >actual_value &&\n+        test_cmp expected_key actual_key &&\n+        test_cmp expected_value actual_value\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522384","messageId":"20250722002835.33428-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250722002835.33428-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 3/5] repo: add field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-22T00:28:33Z","receivedAt":"2025-07-22T00:29:21Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag --is-bare-repository from git-rev-parse is used for retrieving\nwhether the current repository is bare. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field layout.bare to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 4 ++++\n builtin/repo.c              | 9 +++++++++\n t/t1900-repo.sh             | 6 ++++++\n 3 files changed, 19 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex cf8483ec49..7124487323 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -33,6 +33,10 @@ categories:\n Reference-related data:\n * `format`: the reference storage format\n \n+`layout`::\n+Information about the how the current repository is represented:\n+* `bare`: `true` if this is a bare repository, otherwise `false`.\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 5beae0f781..b85bd10889 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,6 +1,9 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n #include \"parse-options.h\"\n #include \"refs.h\"\n+#include \"environment.h\"\n \n typedef const char *get_value_fn(struct repository *repo);\n \n@@ -9,6 +12,11 @@ struct field {\n \tget_value_fn *add_field_callback;\n };\n \n+static const char *get_layout_bare(struct repository *repo UNUSED)\n+{\n+\treturn is_bare_repository() ? \"true\" : \"false\";\n+}\n+\n static const char *get_references_format(struct repository *repo)\n {\n \treturn ref_storage_format_to_name(repo->ref_storage_format);\n@@ -16,6 +24,7 @@ static const char *get_references_format(struct repository *repo)\n \n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n+\t{ \"layout.bare\", get_layout_bare },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex f072c7c67b..03609ffff9 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -36,6 +36,12 @@ test_repo_info 'ref format files is retrieved correctly' '\n test_repo_info 'ref format reftable is retrieved correctly' '\n \tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' '\n+\tgit init --bare repo' 'layout.bare' 'true'\n+\n test_expect_success \"only one value is returned if the same key is requested twice\" '\n \ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n \techo \"references.format\" >expected_key &&\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522385","messageId":"20250722002835.33428-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250722002835.33428-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 4/5] repo: add field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-22T00:28:34Z","receivedAt":"2025-07-22T00:29:24Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--is-shallow-repository` from git-rev-parse is used for\nretrieving whether the repository is shallow. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field `layout.shallow` to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  1 +\n builtin/repo.c              |  7 +++++++\n t/t1900-repo.sh             | 21 +++++++++++++++++++++\n 3 files changed, 29 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 7124487323..375b956d3f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -36,6 +36,7 @@ Reference-related data:\n `layout`::\n Information about the how the current repository is represented:\n * `bare`: `true` if this is a bare repository, otherwise `false`.\n+* `shallow`: `true` if this is a shallow repository, otherwise `false`.\n \n SEE ALSO\n --------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex b85bd10889..490fa9dd49 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -4,6 +4,7 @@\n #include \"parse-options.h\"\n #include \"refs.h\"\n #include \"environment.h\"\n+#include \"shallow.h\"\n \n typedef const char *get_value_fn(struct repository *repo);\n \n@@ -17,6 +18,11 @@ static const char *get_layout_bare(struct repository *repo UNUSED)\n \treturn is_bare_repository() ? \"true\" : \"false\";\n }\n \n+static const char *get_layout_shallow(struct repository *repo)\n+{\n+\treturn is_repository_shallow(repo) ? \"true\" : \"false\";\n+}\n+\n static const char *get_references_format(struct repository *repo)\n {\n \treturn ref_storage_format_to_name(repo->ref_storage_format);\n@@ -25,6 +31,7 @@ static const char *get_references_format(struct repository *repo)\n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n \t{ \"layout.bare\", get_layout_bare },\n+\t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 03609ffff9..c46ace1fd3 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -42,6 +42,20 @@ test_repo_info 'bare repository = false is retrieved correctly' '\n test_repo_info 'bare repository = true is retrieved correctly' '\n \tgit init --bare repo' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.shallow' 'false'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' '\n+\tgit init remote &&\n+\tcd remote &&\n+\techo x >x &&\n+\tgit add x &&\n+\tgit commit -m x &&\n+\tcd .. &&\n+\tgit clone --depth 1 \"file://$PWD/remote\" repo &&\n+\trm -rf remote\n+\t' 'layout.shallow' 'true'\n+\n test_expect_success \"only one value is returned if the same key is requested twice\" '\n \ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n \techo \"references.format\" >expected_key &&\n@@ -53,4 +67,11 @@ test_expect_success \"only one value is returned if the same key is requested twi\n         test_cmp expected_value actual_value\n '\n \n+test_expect_success 'output is returned correctly when two keys are requested' '\n+\ttest_when_finished \"rm -f expect\" &&\n+\tprintf \"layout.bare=false\\nlayout.shallow=false\\n\" >expect &&\n+\tgit repo info layout.shallow layout.bare >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522386","messageId":"20250722002835.33428-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250722002835.33428-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-22T00:28:35Z","receivedAt":"2025-07-22T00:29:27Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the --format flag to git-repo-info. By using this flag, the users\ncan choose the format for obtaining the data they requested.\n\nGiven that this command can be used for generating input for another\napplications and for being read by end users, it requires at least two\nformats: one for being read by humans and other for being read by\nmachines. Some other Git commands also have two output formats, notably\ngit-config which was the inspiration for the two formats that were\nchosen here:\n\n- keyvalue, where the retrieved data is printed one per line, using =\n  for delimiting the key and the value. This is the default format,\n  targeted for end users.\n- null, where the retrieved data is separated by null characters, using\n  the newline character for delimiting the key and the value. This\n  format is targeted for being read by machines.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 13 +++++++++-\n builtin/repo.c              | 49 ++++++++++++++++++++++++++++++++++---\n t/t1900-repo.sh             | 13 ++++++++--\n 3 files changed, 68 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 375b956d3f..5bdc3831a7 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -18,10 +18,21 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n \n COMMANDS\n --------\n-info [<key>...]::\n+info [--format=<format>] [<key>...]::\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n++\n+The output format can be chosen through the flag `--format`. Two formats are\n+supported:\n++\n+* `keyvalue`: output key-value pairs one per line using the `=` character as\n+the delimiter between the key and the value. This is the default.\n+\n+* `null`: similar to `keyvalue`, but using a newline character as the delimiter\n+between the key and the value and using a null character after each value.\n+This format is better suited for being parsed by another applications than\n+`keyvalue`.\n \n INFO KEYS\n ---------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 490fa9dd49..10d02bb3ea 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -8,6 +8,11 @@\n \n typedef const char *get_value_fn(struct repository *repo);\n \n+enum output_format {\n+\tFORMAT_KEYVALUE,\n+\tFORMAT_NULL_TERMINATED,\n+};\n+\n struct field {\n \tconst char *key;\n \tget_value_fn *add_field_callback;\n@@ -61,9 +66,24 @@ static int qsort_strcmp(const void *va, const void *vb)\n \treturn strcmp(a, b);\n }\n \n-static int print_fields(int argc, const char **argv, struct repository *repo)\n+static int print_fields(int argc, const char **argv,\n+\t\t\tstruct repository *repo,\n+\t\t\tenum output_format format)\n {\n \tconst char *last = \"\";\n+\tchar kv_sep;\n+\tchar field_sep;\n+\n+\tswitch (format) {\n+\tcase FORMAT_KEYVALUE:\n+\t\tkv_sep = '=';\n+\t\tfield_sep = '\\n';\n+\t\tbreak;\n+\tcase FORMAT_NULL_TERMINATED:\n+\t\tkv_sep = '\\n';\n+\t\tfield_sep = '\\0';\n+\t\tbreak;\n+\t}\n \n \tQSORT(argv, argc, qsort_strcmp);\n \n@@ -81,17 +101,38 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \t\t\treturn error(\"key %s not found\", key);\n \n \t\tvalue = callback(repo);\n-\t\tprintf(\"%s=%s\\n\", key, value);\n+\t\tprintf(\"%s%c%s%c\", key, kv_sep, value, field_sep);\n \t\tlast = key;\n \t}\n \n \treturn 0;\n }\n \n-static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+static int repo_info(int argc, const char **argv, const char *prefix,\n \t\t     struct repository *repo)\n {\n-\treturn print_fields(argc - 1, argv + 1, repo);\n+\tconst char *format_str = \"keyvalue\";\n+\tenum output_format format;\n+\tconst char *const repo_info_usage[] = {\n+\t\t\"git repo info [<key>...]\",\n+\t\tNULL\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n+\t\t\t   N_(\"output format\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_info_usage, 0);\n+\n+\tif (!strcmp(format_str, \"keyvalue\"))\n+\t\tformat = FORMAT_KEYVALUE;\n+\telse if (!strcmp(format_str, \"null\"))\n+\t\tformat = FORMAT_NULL_TERMINATED;\n+\telse\n+\t\tdie(\"invalid format %s\", format_str);\n+\n+\treturn print_fields(argc, argv, repo, format);\n }\n \n int cmd_repo(int argc, const char **argv, const char *prefix,\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex c46ace1fd3..fdd3491429 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -20,11 +20,20 @@ test_repo_info () {\n \tkey=$3\n \texpected_value=$4\n \n-\ttest_expect_success \"$label\" '\n+\ttest_expect_success \"null-terminated: $label\" '\n+\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\teval \"$init_command\" &&\n+\t\techo \"$expected_value\" | lf_to_nul >expected &&\n+\t\tgit -C repo repo info --format=null \"$key\" >output &&\n+\t\ttail -n 1 output >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+\n+\ttest_expect_success \"key-value: $label\" '\n \t\ttest_when_finished \"rm -rf repo\" &&\n \t\teval \"$init_command\" &&\n \t\techo \"$expected_value\" >expected &&\n-\t\tgit -C repo repo info \"$key\" >output &&\n+\t\tgit -C repo repo info --format=keyvalue \"$key\" >output &&\n \t\tcut -d \"=\" -f 2 <output >actual &&\n \t\ttest_cmp expected actual\n \t'\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522416","messageId":"CAOLa=ZREo19jCj3i+XkRM15AzaAV9ZLOvt42pTiUFmcZpCyS5g@mail.gmail.com","threadId":"63618","inReplyTo":"20250722002835.33428-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-07-22T09:03:25Z","receivedAt":"2025-07-22T09:03:28Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Currently, `git rev-parse` covers a wide range of functionality not\n> directly related to parsing revisions, as its name suggests. Over time,\n> many features like parsing datestrings, options, paths, and others\n> were added to it because there wasn't a more appropriate command\n> to place them.\n>\n> Create a new Git command called `repo`. `git repo` will be the main\n> command for obtaining the information about a repository (such as\n> metadata and metrics), returning them in a machine readable format\n> following the syntax \"field<LF>value<NUL>\".\n>\n\nDoesn't the latter sentence only apply to 'git repo info'? Other\nsub-commands may not follow the field<LF>value<NUL> syntax, no?\n\n> Also declare a subcommand for `repo` called `info`. `git repo info`\n> will bring the functionality of retrieving repository-related\n> information currently returned by `rev-parse`.\n>\n> Add the required tests, documentation and build changes to enable\n> usage of this subcommand.\n>\n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  .gitignore                  |  1 +\n>  Documentation/git-repo.adoc | 38 +++++++++++++++++++++++++++++++++++++\n>  Documentation/meson.build   |  1 +\n>  Makefile                    |  1 +\n>  builtin.h                   |  1 +\n>  builtin/repo.c              | 26 +++++++++++++++++++++++++\n>  command-list.txt            |  1 +\n>  git.c                       |  1 +\n>  meson.build                 |  1 +\n>  9 files changed, 71 insertions(+)\n>  create mode 100644 Documentation/git-repo.adoc\n>  create mode 100644 builtin/repo.c\n>\n> diff --git a/.gitignore b/.gitignore\n> index 04c444404e..1803023427 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -139,6 +139,7 @@\n>  /git-repack\n>  /git-replace\n>  /git-replay\n> +/git-repo\n>  /git-request-pull\n>  /git-rerere\n>  /git-reset\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> new file mode 100644\n> index 0000000000..caee7d8aef\n> --- /dev/null\n> +++ b/Documentation/git-repo.adoc\n> @@ -0,0 +1,38 @@\n> +git-repo(1)\n> +===========\n> +\n> +NAME\n> +----\n> +git-repo - Retrieve information about a repository\n> +\n> +SYNOPSIS\n> +--------\n> +[synopsis]\n> +git repo info [<key>...]\n> +\n> +DESCRIPTION\n> +-----------\n> +This command retrieve repository level information.\n> +\n\ns/retrieve/retrieves\n\n[snip]\n"},{"id":"522417","messageId":"CAOLa=ZT12oLjXc_UPVgD6Vut7tgvQS5=8qzxaxG09UTw_w8sJA@mail.gmail.com","threadId":"63618","inReplyTo":"20250722002835.33428-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-07-22T09:16:59Z","receivedAt":"2025-07-22T09:17:02Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> This commit is part of the series that introduces the new subcommand\n> git-repo-info.\n>\n> The flag `--show-ref-format` from git-rev-parse is used for retrieving\n> the reference format (i.e. `files` or `reftable`). This way, it is\n> used for querying repository metadata, fitting in the purpose of\n> git-repo-info.\n>\n> Then, add a new field `references.format` to the repo-info subcommand\n> containing that information.\n>\n\nNit: I don't think we need the 'Then, ' here, perhaps 'Add ...'.\n\n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  Documentation/git-repo.adoc |  4 ++\n>  builtin/repo.c              | 75 ++++++++++++++++++++++++++++++++++++-\n>  t/meson.build               |  1 +\n>  t/t1900-repo.sh             | 50 +++++++++++++++++++++++++\n>  4 files changed, 128 insertions(+), 2 deletions(-)\n>  create mode 100755 t/t1900-repo.sh\n>\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index caee7d8aef..cf8483ec49 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -29,6 +29,10 @@ INFO KEYS\n>  The set of data that `git repo` can return is grouped into the following\n>  categories:\n>\n> +`references`::\n> +Reference-related data:\n> +* `format`: the reference storage format\n> +\n>  SEE ALSO\n>  --------\n>  linkgit:git-rev-parse[1]\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index d4f01e35e2..5beae0f781 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -1,12 +1,83 @@\n>  #include \"builtin.h\"\n>  #include \"parse-options.h\"\n> +#include \"refs.h\"\n>\n> -static int repo_info(int argc UNUSED, const char **argv UNUSED,\n> -\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n> +typedef const char *get_value_fn(struct repository *repo);\n> +\n> +struct field {\n> +\tconst char *key;\n> +\tget_value_fn *add_field_callback;\n> +};\n> +\n\nShouldn't 'add_field_callback' be renamed, now that we don't add a field\nbut rather return a value?\n\n> +static const char *get_references_format(struct repository *repo)\n> +{\n> +\treturn ref_storage_format_to_name(repo->ref_storage_format);\n> +}\n> +\n> +/* repo_info_fields keys should be in lexicographical order */\n> +static const struct field repo_info_fields[] = {\n> +\t{ \"references.format\", get_references_format },\n> +};\n> +\n> +static int repo_info_fields_cmp(const void *va, const void *vb)\n> +{\n> +\tconst struct field *a = va;\n> +\tconst struct field *b = vb;\n> +\n> +\treturn strcmp(a->key, b->key);\n> +}\n> +\n> +static get_value_fn *get_value_callback(const char *key)\n>  {\n\nNit: A callback generally is a function provided by when a 'fn A' calls\n'fn B', providing a 'fn C' which 'fn A' provides.\n\nHere perhaps we can simply rename this to 'get_value_fn_for_key' or\nsomething?\n\n> +\tconst struct field search_key = { key, NULL };\n> +\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n> +\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n> +\t\t\t\t\t    sizeof(struct field),\n> +\t\t\t\t\t    repo_info_fields_cmp);\n> +\treturn found ? found->add_field_callback : NULL;\n> +}\n> +\n> +static int qsort_strcmp(const void *va, const void *vb)\n> +{\n> +\tconst char *a = *(const char **)va;\n> +\tconst char *b = *(const char **)vb;\n> +\n> +\treturn strcmp(a, b);\n> +}\n> +\n> +static int print_fields(int argc, const char **argv, struct repository *repo)\n> +{\n> +\tconst char *last = \"\";\n> +\n> +\tQSORT(argv, argc, qsort_strcmp);\n> +\n> +\tfor (int i = 0; i < argc; i++) {\n> +\t\tget_value_fn *callback;\n> +\t\tconst char *key = argv[i];\n> +\t\tconst char *value;\n> +\n> +\t\tif (!strcmp(key, last))\n> +\t\t\tcontinue;\n> +\n> +\t\tcallback = get_value_callback(key);\n> +\n> +\t\tif (!callback)\n> +\t\t\treturn error(\"key %s not found\", key);\n> +\n> +\t\tvalue = callback(repo);\n> +\t\tprintf(\"%s=%s\\n\", key, value);\n\nI like this a lot, since we can simply modify this in the future for\ndifferent formats. Nice!\n\n[snip]\n"},{"id":"522420","messageId":"CAOLa=ZThUFbTyqNXYm=vRtwaoqu4y+Mc24VvQ-QA4-HzhcSo3g@mail.gmail.com","threadId":"63618","inReplyTo":"20250722002835.33428-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-07-22T09:26:51Z","receivedAt":"2025-07-22T09:26:53Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add the --format flag to git-repo-info. By using this flag, the users\n> can choose the format for obtaining the data they requested.\n>\n> Given that this command can be used for generating input for another\n\nNit: s/another/other\n\n> applications and for being read by end users, it requires at least two\n> formats: one for being read by humans and other for being read by\n> machines. Some other Git commands also have two output formats, notably\n> git-config which was the inspiration for the two formats that were\n> chosen here:\n>\n> - keyvalue, where the retrieved data is printed one per line, using =\n>   for delimiting the key and the value. This is the default format,\n>   targeted for end users.\n> - null, where the retrieved data is separated by null characters, using\n>   the newline character for delimiting the key and the value. This\n>   format is targeted for being read by machines.\n>\n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  Documentation/git-repo.adoc | 13 +++++++++-\n>  builtin/repo.c              | 49 ++++++++++++++++++++++++++++++++++---\n>  t/t1900-repo.sh             | 13 ++++++++--\n>  3 files changed, 68 insertions(+), 7 deletions(-)\n>\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index 375b956d3f..5bdc3831a7 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -18,10 +18,21 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n>\n>  COMMANDS\n>  --------\n> -info [<key>...]::\n> +info [--format=<format>] [<key>...]::\n>  \tRetrieve metadata-related information about the current repository. Only\n>  \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n>  \tsection below).\n> ++\n> +The output format can be chosen through the flag `--format`. Two formats are\n> +supported:\n> ++\n\nIt's hard to know where the documentation for '--format' starts from,\nperhaps we can take a note from how 'Documentation/git-refs.adoc' does\nthis?\n\n[snip]\n\n>  int cmd_repo(int argc, const char **argv, const char *prefix,\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> index c46ace1fd3..fdd3491429 100755\n> --- a/t/t1900-repo.sh\n> +++ b/t/t1900-repo.sh\n> @@ -20,11 +20,20 @@ test_repo_info () {\n>  \tkey=$3\n>  \texpected_value=$4\n>\n> -\ttest_expect_success \"$label\" '\n> +\ttest_expect_success \"null-terminated: $label\" '\n> +\t\ttest_when_finished \"rm -rf repo\" &&\n> +\t\teval \"$init_command\" &&\n> +\t\techo \"$expected_value\" | lf_to_nul >expected &&\n> +\t\tgit -C repo repo info --format=null \"$key\" >output &&\n> +\t\ttail -n 1 output >actual &&\n> +\t\ttest_cmp expected actual\n> +\t'\n> +\n> +\ttest_expect_success \"key-value: $label\" '\n>  \t\ttest_when_finished \"rm -rf repo\" &&\n>  \t\teval \"$init_command\" &&\n>  \t\techo \"$expected_value\" >expected &&\n> -\t\tgit -C repo repo info \"$key\" >output &&\n> +\t\tgit -C repo repo info --format=keyvalue \"$key\" >output &&\n>  \t\tcut -d \"=\" -f 2 <output >actual &&\n>  \t\ttest_cmp expected actual\n>  \t'\n\nShouldn't we also test for invalid format? Also perhaps invalid key in\nthe first commit too.\n"},{"id":"522448","messageId":"xmqqtt34tfna.fsf@gitster.g","threadId":"63618","inReplyTo":"CAOLa=ZREo19jCj3i+XkRM15AzaAV9ZLOvt42pTiUFmcZpCyS5g@mail.gmail.com","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-22T15:21:45Z","receivedAt":"2025-07-22T15:21:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n>\n>> Currently, `git rev-parse` covers a wide range of functionality not\n>> directly related to parsing revisions, as its name suggests. Over time,\n>> many features like parsing datestrings, options, paths, and others\n>> were added to it because there wasn't a more appropriate command\n>> to place them.\n>>\n>> Create a new Git command called `repo`. `git repo` will be the main\n>> command for obtaining the information about a repository (such as\n>> metadata and metrics), returning them in a machine readable format\n>> following the syntax \"field<LF>value<NUL>\".\n>>\n>\n> Doesn't the latter sentence only apply to 'git repo info'? Other\n> sub-commands may not follow the field<LF>value<NUL> syntax, no?\n\nTrue.\n\nI also wonder who it helps to use <LF> as a field separator.  Once\nwe require consumers to properly handle <NUL>, it does not make it\neasier to write such a consumer script if the format uses <LF>\nthere, does it?  Besides, wouldn't it possible that field may have\nto contain any end-user specified key, including <LF>?  If so, we'd\nneed to have some quoting/unquoting mechanism in the syntax anyway,\nso the behefit of using <NUL> to simplify the parser would already\nbe lost.\n\nThanks.\n"},{"id":"522492","messageId":"ldomqfgzts2fs3zuzuyfpsp4jsuec7a6ooisztqx6pe2373jzx@mqzh62weo2jm","threadId":"63618","inReplyTo":"20250722002835.33428-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Justin Tobler","fromEmail":"jltobler@gmail.com","sentAt":"2025-07-22T19:25:45Z","receivedAt":"2025-07-22T19:31:35Z","isPatch":true,"sender":{"key":"jltobler@gmail.com","avatar":"https://avatars.githubusercontent.com/u/53454972?v=4"},"body":"On 25/07/21 09:28PM, Lucas Seiki Oshiro wrote:\n> This commit is part of the series that introduces the new subcommand\n> git-repo-info.\n> \n> The flag `--show-ref-format` from git-rev-parse is used for retrieving\n> the reference format (i.e. `files` or `reftable`). This way, it is\n> used for querying repository metadata, fitting in the purpose of\n> git-repo-info.\n> \n> Then, add a new field `references.format` to the repo-info subcommand\n> containing that information.\n> \n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  Documentation/git-repo.adoc |  4 ++\n>  builtin/repo.c              | 75 ++++++++++++++++++++++++++++++++++++-\n>  t/meson.build               |  1 +\n>  t/t1900-repo.sh             | 50 +++++++++++++++++++++++++\n>  4 files changed, 128 insertions(+), 2 deletions(-)\n>  create mode 100755 t/t1900-repo.sh\n> \n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index caee7d8aef..cf8483ec49 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -29,6 +29,10 @@ INFO KEYS\n>  The set of data that `git repo` can return is grouped into the following\n>  categories:\n>  \n> +`references`::\n> +Reference-related data:\n> +* `format`: the reference storage format\n> +\n>  SEE ALSO\n>  --------\n>  linkgit:git-rev-parse[1]\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index d4f01e35e2..5beae0f781 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -1,12 +1,83 @@\n>  #include \"builtin.h\"\n>  #include \"parse-options.h\"\n> +#include \"refs.h\"\n>  \n> -static int repo_info(int argc UNUSED, const char **argv UNUSED,\n> -\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n> +typedef const char *get_value_fn(struct repository *repo);\n> +\n> +struct field {\n> +\tconst char *key;\n> +\tget_value_fn *add_field_callback;\n> +};\n> +\n> +static const char *get_references_format(struct repository *repo)\n> +{\n> +\treturn ref_storage_format_to_name(repo->ref_storage_format);\n> +}\n> +\n> +/* repo_info_fields keys should be in lexicographical order */\n> +static const struct field repo_info_fields[] = {\n> +\t{ \"references.format\", get_references_format },\n> +};\n\nOk, so each key has a corresponding callback that is used to get its\nvalue. This works fine when we have one operation/callback per key, but\nI could see this being a bit inflexible in cases where performing a\nsingle operation could be expected to generate multiple keys worth of\ninformation at a time.\n\nI certainly see this being the case with git-repo-stats where, for\nexample, interating over references will produce multiple keyvalues\nindicating the number of branches, tags, remotes, etc. But, maybe for\ngit-repo-info this will not be as much of a concern?\n\n> +\n> +static int repo_info_fields_cmp(const void *va, const void *vb)\n> +{\n> +\tconst struct field *a = va;\n> +\tconst struct field *b = vb;\n> +\n> +\treturn strcmp(a->key, b->key);\n> +}\n> +\n> +static get_value_fn *get_value_callback(const char *key)\n>  {\n> +\tconst struct field search_key = { key, NULL };\n> +\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n> +\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n> +\t\t\t\t\t    sizeof(struct field),\n> +\t\t\t\t\t    repo_info_fields_cmp);\n> +\treturn found ? found->add_field_callback : NULL;\n> +}\n> +\n> +static int qsort_strcmp(const void *va, const void *vb)\n> +{\n> +\tconst char *a = *(const char **)va;\n> +\tconst char *b = *(const char **)vb;\n> +\n> +\treturn strcmp(a, b);\n> +}\n> +\n> +static int print_fields(int argc, const char **argv, struct repository *repo)\n> +{\n> +\tconst char *last = \"\";\n> +\n> +\tQSORT(argv, argc, qsort_strcmp);\n> +\n> +\tfor (int i = 0; i < argc; i++) {\n> +\t\tget_value_fn *callback;\n> +\t\tconst char *key = argv[i];\n> +\t\tconst char *value;\n> +\n> +\t\tif (!strcmp(key, last))\n> +\t\t\tcontinue;\n> +\n> +\t\tcallback = get_value_callback(key);\n> +\n> +\t\tif (!callback)\n> +\t\t\treturn error(\"key %s not found\", key);\n> +\n> +\t\tvalue = callback(repo);\n> +\t\tprintf(\"%s=%s\\n\", key, value);\n> +\t\tlast = key;\n> +\t}\n\nIf the user does not input any keys, we simply do nothing. I do wonder\nif this is really the best default behavior. Maybe instead we should\nerror out? Or maybe treat it as though all keys were requested?\n\n-Justin\n\n> +\n>  \treturn 0;\n>  }\n>  \n> +static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n> +\t\t     struct repository *repo)\n> +{\n> +\treturn print_fields(argc - 1, argv + 1, repo);\n> +}\n> +\n>  int cmd_repo(int argc, const char **argv, const char *prefix,\n>  \t     struct repository *repo)\n>  {\n"},{"id":"522570","messageId":"7d6ad7cb-e25a-41de-9588-6a3c1b0717e8@gmail.com","threadId":"63618","inReplyTo":"ldomqfgzts2fs3zuzuyfpsp4jsuec7a6ooisztqx6pe2373jzx@mqzh62weo2jm","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-07-23T14:53:50Z","receivedAt":"2025-07-23T14:54:05Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 22/07/2025 20:25, Justin Tobler wrote:\n> On 25/07/21 09:28PM, Lucas Seiki Oshiro wrote:\n> \n>> +typedef const char *get_value_fn(struct repository *repo);\n>> +\n>> +struct field {\n>> +\tconst char *key;\n>> +\tget_value_fn *add_field_callback;\n>> +};\n>> +\n>> +static const char *get_references_format(struct repository *repo)\n>> +{\n>> +\treturn ref_storage_format_to_name(repo->ref_storage_format);\n>> +}\n>> +\n>> +/* repo_info_fields keys should be in lexicographical order */\n>> +static const struct field repo_info_fields[] = {\n>> +\t{ \"references.format\", get_references_format },\n>> +};\n> \n> Ok, so each key has a corresponding callback that is used to get its\n> value. This works fine when we have one operation/callback per key, but\n> I could see this being a bit inflexible in cases where performing a\n> single operation could be expected to generate multiple keys worth of\n> information at a time.\n> \n> I certainly see this being the case with git-repo-stats where, for\n> example, interating over references will produce multiple keyvalues\n> indicating the number of branches, tags, remotes, etc. But, maybe for\n> git-repo-info this will not be as much of a concern?\n\nI think the fact that git_value_fn returns 'const char*' is a concern as \nit means we cannot return an allocated string. It would be better to \npass a 'struct strbuf' to the callback and write the value to that \ninstead. That way a callback can create the value piecemeal if needed \nand we don't have to worry about whether we should be free()ing the \nreturned string.\n\nAn alternative approach would be to pass a function pointer to the \ncallback which it then calls with the key and value to produce the output.\n\nThanks\n\nPhillip\n>> +\n>> +static int repo_info_fields_cmp(const void *va, const void *vb)\n>> +{\n>> +\tconst struct field *a = va;\n>> +\tconst struct field *b = vb;\n>> +\n>> +\treturn strcmp(a->key, b->key);\n>> +}\n>> +\n>> +static get_value_fn *get_value_callback(const char *key)\n>>   {\n>> +\tconst struct field search_key = { key, NULL };\n>> +\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n>> +\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n>> +\t\t\t\t\t    sizeof(struct field),\n>> +\t\t\t\t\t    repo_info_fields_cmp);\n>> +\treturn found ? found->add_field_callback : NULL;\n>> +}\n>> +\n>> +static int qsort_strcmp(const void *va, const void *vb)\n>> +{\n>> +\tconst char *a = *(const char **)va;\n>> +\tconst char *b = *(const char **)vb;\n>> +\n>> +\treturn strcmp(a, b);\n>> +}\n>> +\n>> +static int print_fields(int argc, const char **argv, struct repository *repo)\n>> +{\n>> +\tconst char *last = \"\";\n>> +\n>> +\tQSORT(argv, argc, qsort_strcmp);\n>> +\n>> +\tfor (int i = 0; i < argc; i++) {\n>> +\t\tget_value_fn *callback;\n>> +\t\tconst char *key = argv[i];\n>> +\t\tconst char *value;\n>> +\n>> +\t\tif (!strcmp(key, last))\n>> +\t\t\tcontinue;\n>> +\n>> +\t\tcallback = get_value_callback(key);\n>> +\n>> +\t\tif (!callback)\n>> +\t\t\treturn error(\"key %s not found\", key);\n>> +\n>> +\t\tvalue = callback(repo);\n>> +\t\tprintf(\"%s=%s\\n\", key, value);\n>> +\t\tlast = key;\n>> +\t}\n> \n> If the user does not input any keys, we simply do nothing. I do wonder\n> if this is really the best default behavior. Maybe instead we should\n> error out? Or maybe treat it as though all keys were requested?\n> \n> -Justin\n> \n>> +\n>>   \treturn 0;\n>>   }\n>>   \n>> +static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n>> +\t\t     struct repository *repo)\n>> +{\n>> +\treturn print_fields(argc - 1, argv + 1, repo);\n>> +}\n>> +\n>>   int cmd_repo(int argc, const char **argv, const char *prefix,\n>>   \t     struct repository *repo)\n>>   {\n\n"},{"id":"522575","messageId":"999D2330-D15E-4137-B9CD-8FF9880DCAB6@gmail.com","threadId":"63618","inReplyTo":"CAOLa=ZREo19jCj3i+XkRM15AzaAV9ZLOvt42pTiUFmcZpCyS5g@mail.gmail.com","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-23T15:49:38Z","receivedAt":"2025-07-23T15:49:53Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Doesn't the latter sentence only apply to 'git repo info'?\n\nYes. I'll fix this commit message.\n\n"},{"id":"522576","messageId":"ABD6739A-EBE1-4228-BDAA-600104726808@gmail.com","threadId":"63618","inReplyTo":"xmqqtt34tfna.fsf@gitster.g","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-23T16:28:47Z","receivedAt":"2025-07-23T16:29:01Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> I also wonder who it helps to use <LF> as a field separator.\n\nDo you mean the <LF> between the field and the value? If so, it was the\nformat suggested by Phillip [1]. I accepted the suggestion because:\n\n1. It's easy to be parsed\n2. It's easy to write (then I could drop the json_write stuff)\n3. It's already used by an existing command (git-config)\n\n> Once we require consumers to properly handle <NUL>, it does not make\n> it easier to write such a consumer script if the format uses <LF>\n> there, does it?\n\nNot much... Since the keys contain only letters and dots, any other\ncharacter would work here, but I wanted to be consistent with\nsomething that already exists.\n\nHowever, to the best of my knowledge, we have several null-terminated\ncommands (git ls-files -z, git status -z, git ls-tree -z, etc), but\nfew commands that use LF as a key-value separator (I only remember\ngit config -z).\n\n> Besides, wouldn't it possible that field may have to contain any\n> end-user specified key, including <LF>?\n\nIn `repo info`, no. This way, it's safe to parse everything before\nthe first <LF> as the key and everything between <LF> and <NUL> as\nthe value.\n\nThe value may contain a <LF>, however, the first <LF> would still\nbe the separator.\n\nThanks!\n\n[1] https://lore.kernel.org/git/223c7cbd-610e-49e2-90e2-5914cbc0f1d7@gmail.com/"},{"id":"522587","messageId":"4A044F64-8C3F-41FB-B079-AE10B42539ED@gmail.com","threadId":"63618","inReplyTo":"7d6ad7cb-e25a-41de-9588-6a3c1b0717e8@gmail.com","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-23T17:44:52Z","receivedAt":"2025-07-23T17:45:07Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> I think the fact that git_value_fn returns 'const char*' is a\n> concern as it means we cannot return an allocated string.\n\nYeah, I was thinking about that. It is not a problem by now, but\nafter the acceptance of this patchset I'll start to work in the\n`path.*` fields and that would be a problem.\n\n> It would be better to pass a 'struct strbuf' to the callback and\n> write the value to that instead.\n\nWhen I was working on this version I was thinking about it, but I\nleft the way it is to make it simpler, and refactor it when\nimplementing the `path.*` fields. But now I think it would be\nbetter to change it in v6.\n\n"},{"id":"522589","messageId":"xmqq34amol1l.fsf@gitster.g","threadId":"63618","inReplyTo":"ABD6739A-EBE1-4228-BDAA-600104726808@gmail.com","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-23T17:48:38Z","receivedAt":"2025-07-23T17:48:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n>> Besides, wouldn't it possible that field may have to contain any\n>> end-user specified key, including <LF>?\n>\n> In `repo info`, no. This way, it's safe to parse everything before\n> the first <LF> as the key and everything between <LF> and <NUL> as\n> the value.\n\nOK.  Even \"git config\" punts there by restricting the subsection\npart of the configuration variable name by forbidding LF and NUL in\nit ;-)  So, I guess we are OK here.\n\nThanks for thinking it through.\n"},{"id":"522598","messageId":"B2B2D8E4-DD78-4ACB-921D-A8747A0AB0CF@gmail.com","threadId":"63618","inReplyTo":"ldomqfgzts2fs3zuzuyfpsp4jsuec7a6ooisztqx6pe2373jzx@mqzh62weo2jm","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-23T18:26:14Z","receivedAt":"2025-07-23T18:26:29Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Ok, so each key has a corresponding callback that is used to get its\n> value. This works fine when we have one operation/callback per key, but\n> I could see this being a bit inflexible in cases where performing a\n> single operation could be expected to generate multiple keys worth of\n> information at a time.\n\nFor git-repo-info it wouldn't be a problem, as we return one value per\nrequested key.\n\nHowever, I plan to add a feature in the future for requesting a group\nof fields. For example:\n\n$ git repo info layout\nlayout.bare=true\nlayout.shallow=false\n\nwhich, of course, is not what exactly you mean :-). But it is a similar\nproblem of requesting one key and getting several key-value pairs.\n\n> I certainly see this being the case with git-repo-stats where, for\n> example, interating over references will produce multiple keyvalues\n> indicating the number of branches, tags, remotes, etc. But, maybe for\n> git-repo-info this will not be as much of a concern?\n\nFor git-repo-info it isn't, at least by the planned set of values.\nHowever, given that I have plans to add the previous feature, I can\ntry to make it flexible for doing things like:\n\n$ git repo stats object.count\nobject.count.blob=123\nobject.count.tree=456\nobject.count.commit=789\n\nSo you can use the same code for outputting git-repo-stats data. By\nnow, I plan to finish the basic functionality (i.e. finishing this\n\"skeleton\" and adding the rest of the proposed fields), then I'll\nthink about this feature.\n\nI think the current solution is simple enough to being refactored to\nsupport this kind of thing in the future.\n\n> If the user does not input any keys, we simply do nothing. I do wonder\n> if this is really the best default behavior.\n\nIn previous versions it returned all the available fields. There was a\n`--allow-empty` flag that allowed to request no field.\n\nAfter the discussion in [1] we agreed that it would be better to return\nnothing by default and add a `--all` flag for returning all the fields\n(or a default set of fields).\n\n"},{"id":"522603","messageId":"12708774.O9o76ZdvQC@cayenne","threadId":"63618","inReplyTo":"20250722002835.33428-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Jean-Noël AVILA","fromEmail":"jn.avila@free.fr","sentAt":"2025-07-23T20:03:12Z","receivedAt":"2025-07-23T20:03:29Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"On Tuesday, 22 July 2025 02:28:31 CEST Lucas Seiki Oshiro wrote:\n> Currently, `git rev-parse` covers a wide range of functionality not\n> directly related to parsing revisions, as its name suggests. Over time,\n> many features like parsing datestrings, options, paths, and others\n> were added to it because there wasn't a more appropriate command\n> to place them.\n> \n> Create a new Git command called `repo`. `git repo` will be the main\n> command for obtaining the information about a repository (such as\n> metadata and metrics), returning them in a machine readable format\n> following the syntax \"field<LF>value<NUL>\".\n> \n> Also declare a subcommand for `repo` called `info`. `git repo info`\n> will bring the functionality of retrieving repository-related\n> information currently returned by `rev-parse`.\n> \n> Add the required tests, documentation and build changes to enable\n> usage of this subcommand.\n> \n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  .gitignore                  |  1 +\n>  Documentation/git-repo.adoc | 38 +++++++++++++++++++++++++++++++++++++\n>  Documentation/meson.build   |  1 +\n>  Makefile                    |  1 +\n>  builtin.h                   |  1 +\n>  builtin/repo.c              | 26 +++++++++++++++++++++++++\n>  command-list.txt            |  1 +\n>  git.c                       |  1 +\n>  meson.build                 |  1 +\n>  9 files changed, 71 insertions(+)\n>  create mode 100644 Documentation/git-repo.adoc\n>  create mode 100644 builtin/repo.c\n> \n> diff --git a/.gitignore b/.gitignore\n> index 04c444404e..1803023427 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -139,6 +139,7 @@\n>  /git-repack\n>  /git-replace\n>  /git-replay\n> +/git-repo\n>  /git-request-pull\n>  /git-rerere\n>  /git-reset\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> new file mode 100644\n> index 0000000000..caee7d8aef\n> --- /dev/null\n> +++ b/Documentation/git-repo.adoc\n> @@ -0,0 +1,38 @@\n> +git-repo(1)\n> +===========\n> +\n> +NAME\n> +----\n> +git-repo - Retrieve information about a repository\n> +\n> +SYNOPSIS\n> +--------\n> +[synopsis]\n> +git repo info [<key>...]\n> +\n> +DESCRIPTION\n> +-----------\n> +This command retrieve repository level information.\n> +\n> +THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n> +\n> +COMMANDS\n> +--------\n> +info [<key>...]::\n\nThe commands and options are quoted with back-quotes with the new synopsis \nstyle:\n\n`info [<key> ...]`::\n\nThanks\n\nJN\n\n\n"},{"id":"522626","messageId":"aIHRCz_qswp7RgSy@pks.im","threadId":"63618","inReplyTo":"xmqqtt34tfna.fsf@gitster.g","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-24T06:22:03Z","receivedAt":"2025-07-24T06:22:11Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Jul 22, 2025 at 08:21:45AM -0700, Junio C Hamano wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n> \n> > Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n> >\n> >> Currently, `git rev-parse` covers a wide range of functionality not\n> >> directly related to parsing revisions, as its name suggests. Over time,\n> >> many features like parsing datestrings, options, paths, and others\n> >> were added to it because there wasn't a more appropriate command\n> >> to place them.\n> >>\n> >> Create a new Git command called `repo`. `git repo` will be the main\n> >> command for obtaining the information about a repository (such as\n> >> metadata and metrics), returning them in a machine readable format\n> >> following the syntax \"field<LF>value<NUL>\".\n> >>\n> >\n> > Doesn't the latter sentence only apply to 'git repo info'? Other\n> > sub-commands may not follow the field<LF>value<NUL> syntax, no?\n> \n> True.\n> \n> I also wonder who it helps to use <LF> as a field separator.  Once\n> we require consumers to properly handle <NUL>, it does not make it\n> easier to write such a consumer script if the format uses <LF>\n> there, does it?  Besides, wouldn't it possible that field may have\n> to contain any end-user specified key, including <LF>?  If so, we'd\n> need to have some quoting/unquoting mechanism in the syntax anyway,\n> so the behefit of using <NUL> to simplify the parser would already\n> be lost.\n\nScripts should always use NUL, true. But sometimes a user may want to\ninspect these key-value pairs, as well, just to double check a certain\nproperty of the repository, or to figure out how a certain property\nlooks like while writing a script that parses the same key-value but\nNUL-separated pairs. Using NUL bytes would be a bit of a pain in that\nsituation.\n\nI'm not really too sure whether we need to bother with quoting. The\nLF-separated output shouldn't ever be used in a script, so I don't mind\ntoo much whether it always works. But I guess it wouldn't be hard either\nto just have something like:\n\n    if (uses_newline)\n        quote_c_style(...);\n\nSo with that in mind it's probably better to just do the right thing.\n\nPatrick\n"},{"id":"522627","messageId":"aIHRETKuPM6gGErZ@pks.im","threadId":"63618","inReplyTo":"20250722002835.33428-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-24T06:22:09Z","receivedAt":"2025-07-24T06:22:15Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Jul 21, 2025 at 09:28:32PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index d4f01e35e2..5beae0f781 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -1,12 +1,83 @@\n>  #include \"builtin.h\"\n>  #include \"parse-options.h\"\n> +#include \"refs.h\"\n>  \n> -static int repo_info(int argc UNUSED, const char **argv UNUSED,\n> -\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n> +typedef const char *get_value_fn(struct repository *repo);\n> +\n> +struct field {\n> +\tconst char *key;\n> +\tget_value_fn *add_field_callback;\n> +};\n> +\n> +static const char *get_references_format(struct repository *repo)\n> +{\n> +\treturn ref_storage_format_to_name(repo->ref_storage_format);\n> +}\n> +\n> +/* repo_info_fields keys should be in lexicographical order */\n> +static const struct field repo_info_fields[] = {\n> +\t{ \"references.format\", get_references_format },\n> +};\n\nOne problem that we'll eventually face is that we want to add a callback\nthat needs to return an allocated string. With the current design we\ncannot really handle that without creating amemory leak. So I guess it\nwould make more sense if the callback thus received a pointer to a\nstrbuf that it is expected to print its value to.\n\nAlso, we should be able to return errors. While that's not needed right\nnow, it may be in the future. So how about:\n\n\ttypedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n\n\tstatic int get_references_format(struct repository *repo,\n\t\t\t\t\t struct strbuf *buf)\n\t{\n\t\tstrbuf_addstr(buf, ref_storage_format_to_name(repo->ref_storage_format));\n\t\treturn 0;\n\t}\n\n> +static int repo_info_fields_cmp(const void *va, const void *vb)\n> +{\n> +\tconst struct field *a = va;\n> +\tconst struct field *b = vb;\n> +\n> +\treturn strcmp(a->key, b->key);\n> +}\n> +\n> +static get_value_fn *get_value_callback(const char *key)\n>  {\n> +\tconst struct field search_key = { key, NULL };\n> +\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n> +\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n> +\t\t\t\t\t    sizeof(struct field),\n\nNit: let's rather use `sizeof(*repo_info_fields)`. Makes it more\ntrivially correct without having to double check whether\n`repo_info_fields` actually is a `struct field`..\n\nPatrick\n"},{"id":"522628","messageId":"aIHRFkAdrdPZWrll@pks.im","threadId":"63618","inReplyTo":"20250722002835.33428-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-24T06:22:14Z","receivedAt":"2025-07-24T06:22:20Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Jul 21, 2025 at 09:28:35PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index 375b956d3f..5bdc3831a7 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -18,10 +18,21 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n>  \n>  COMMANDS\n>  --------\n> -info [<key>...]::\n> +info [--format=<format>] [<key>...]::\n>  \tRetrieve metadata-related information about the current repository. Only\n>  \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n>  \tsection below).\n> ++\n> +The output format can be chosen through the flag `--format`. Two formats are\n> +supported:\n> ++\n> +* `keyvalue`: output key-value pairs one per line using the `=` character as\n> +the delimiter between the key and the value. This is the default.\n> +\n> +* `null`: similar to `keyvalue`, but using a newline character as the delimiter\n\nOur manual typically says \"NUL\"-terminated format in other places, like\nfor example in git-update-ref(1). So let's rather name the format `nul`.\n\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index 490fa9dd49..10d02bb3ea 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -8,6 +8,11 @@\n>  \n>  typedef const char *get_value_fn(struct repository *repo);\n>  \n> +enum output_format {\n> +\tFORMAT_KEYVALUE,\n> +\tFORMAT_NULL_TERMINATED,\n\nLikewise, this should be `FORMAT_NUL_TERMINATED`.\n\n> @@ -61,9 +66,24 @@ static int qsort_strcmp(const void *va, const void *vb)\n>  \treturn strcmp(a, b);\n>  }\n>  \n> -static int print_fields(int argc, const char **argv, struct repository *repo)\n> +static int print_fields(int argc, const char **argv,\n> +\t\t\tstruct repository *repo,\n> +\t\t\tenum output_format format)\n>  {\n>  \tconst char *last = \"\";\n> +\tchar kv_sep;\n> +\tchar field_sep;\n> +\n> +\tswitch (format) {\n> +\tcase FORMAT_KEYVALUE:\n> +\t\tkv_sep = '=';\n> +\t\tfield_sep = '\\n';\n> +\t\tbreak;\n> +\tcase FORMAT_NULL_TERMINATED:\n> +\t\tkv_sep = '\\n';\n> +\t\tfield_sep = '\\0';\n> +\t\tbreak;\n> +\t}\n>  \n>  \tQSORT(argv, argc, qsort_strcmp);\n>  \n\nMakes sense.\n\n> @@ -81,17 +101,38 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n>  \t\t\treturn error(\"key %s not found\", key);\n>  \n>  \t\tvalue = callback(repo);\n> -\t\tprintf(\"%s=%s\\n\", key, value);\n> +\t\tprintf(\"%s%c%s%c\", key, kv_sep, value, field_sep);\n>  \t\tlast = key;\n\nThis here is where we can introduce the quoting. Something like:\n\n\tif (format == FORMAT_KEYVALUE) {\n\t\tstrbuf_reset(&quoted_value);\n\t\tquote_c_style(value, &quoted_value, NULL, 0);\n\t}\n\n\tprintf(\"%s%c%s%c\", key, kv_sep,\n\t       format == FORMAT_KEYVALUE ? quoted_value.buf : value,\n\t       field_sep);\n\n>  \t}\n>  \n>  \treturn 0;\n>  }\n>  \n> -static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n> +static int repo_info(int argc, const char **argv, const char *prefix,\n>  \t\t     struct repository *repo)\n>  {\n> -\treturn print_fields(argc - 1, argv + 1, repo);\n> +\tconst char *format_str = \"keyvalue\";\n> +\tenum output_format format;\n> +\tconst char *const repo_info_usage[] = {\n> +\t\t\"git repo info [<key>...]\",\n> +\t\tNULL\n> +\t};\n> +\tstruct option options[] = {\n> +\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n> +\t\t\t   N_(\"output format\")),\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\targc = parse_options(argc, argv, prefix, options, repo_info_usage, 0);\n> +\n> +\tif (!strcmp(format_str, \"keyvalue\"))\n> +\t\tformat = FORMAT_KEYVALUE;\n> +\telse if (!strcmp(format_str, \"null\"))\n> +\t\tformat = FORMAT_NULL_TERMINATED;\n> +\telse\n> +\t\tdie(\"invalid format %s\", format_str);\n\nNit: this should be translated and quote the format string:\n\n\tdie(_(\"invalid format: '%s'\"), format_str);\n\nPatrick\n"},{"id":"522685","messageId":"xmqqwm7xindq.fsf@gitster.g","threadId":"63618","inReplyTo":"aIHRCz_qswp7RgSy@pks.im","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-24T16:06:57Z","receivedAt":"2025-07-24T16:07:00Z","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>> True.\n>> \n>> I also wonder who it helps to use <LF> as a field separator.  Once\n>> we require consumers to properly handle <NUL>, it does not make it\n>> easier to write such a consumer script if the format uses <LF>\n>> there, does it?  Besides, wouldn't it possible that field may have\n>> to contain any end-user specified key, including <LF>?  If so, we'd\n>> need to have some quoting/unquoting mechanism in the syntax anyway,\n>> so the behefit of using <NUL> to simplify the parser would already\n>> be lost.\n>\n> Scripts should always use NUL, true. But sometimes a user may want to\n> inspect these key-value pairs, as well, just to double check a certain\n> property of the repository, or to figure out how a certain property\n> looks like while writing a script that parses the same key-value but\n> NUL-separated pairs. Using NUL bytes would be a bit of a pain in that\n> situation.\n\nTrue.  I do not think the discussion so far had much focus on\nhelping human users doing ad-hoc inspection, and NUL can be annoying\nfor such use cases (even though tr is your friend).\n\n> I'm not really too sure whether we need to bother with quoting. The\n> LF-separated output shouldn't ever be used in a script, so I don't mind\n> too much whether it always works. But I guess it wouldn't be hard either\n> to just have something like:\n>\n>     if (uses_newline)\n>         quote_c_style(...);\n>\n> So with that in mind it's probably better to just do the right thing.\n\nThe right thing being...?  Use <LF> as a record separator to avoid\nforcing <NUL> on possible human readers, and adopt quoting in a rare\ncase where <LF>s or <NUL>s need to be in the payload?  Or something\nelse?\n\nThanks.\n\n"},{"id":"522714","messageId":"aIMRrV3Atv35xeXj@pks.im","threadId":"63618","inReplyTo":"xmqqwm7xindq.fsf@gitster.g","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-25T05:10:05Z","receivedAt":"2025-07-25T05:10:12Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jul 24, 2025 at 09:06:57AM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> > I'm not really too sure whether we need to bother with quoting. The\n> > LF-separated output shouldn't ever be used in a script, so I don't mind\n> > too much whether it always works. But I guess it wouldn't be hard either\n> > to just have something like:\n> >\n> >     if (uses_newline)\n> >         quote_c_style(...);\n> >\n> > So with that in mind it's probably better to just do the right thing.\n> \n> The right thing being...?  Use <LF> as a record separator to avoid\n> forcing <NUL> on possible human readers, and adopt quoting in a rare\n> case where <LF>s or <NUL>s need to be in the payload?  Or something\n> else?\n\nUse <LF> by default to help humans, but do C-style quoting in that case.\nProvide <NUL> terminated output format for machines so that they don't\nhave to care about unquoting.\n\nPatrick\n"},{"id":"522813","messageId":"1642D169-F952-4DD6-8D95-50D5F3C400F1@gmail.com","threadId":"63618","inReplyTo":"aIHRCz_qswp7RgSy@pks.im","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-26T21:54:15Z","receivedAt":"2025-07-26T21:54:31Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> I'm not really too sure whether we need to bother with quoting.\n\nGiven the set of values that I have planned for this command, the only\nproblem that I see is in the (near) future where `git-repo-info` will\nreturn paths and only when those paths have less common bytes in those\npaths (e.g. newlines).\n\nI think I'll leave the way it is by now because it won't affect the\ncurrent set of values (which are \"true\", \"false\", \"files\" and\n\"reftable\"), and we can discuss more about it in the patches related to\npaths.\n\n> So with that in mind it's probably better to just do the right thing.\n\nHmmm... I'm thinking about what is the \"right thing\" in this context.\nFor example, `git config --list` doesn't quote when in its key=value\nformat. Perhaps I'm citing `git config --list` too much :-), but it is\nbecause is the closer command to `git-repo-info` in that sense.\n\nBut by now, I think we can discuss it later. Adding the quote_c_style\nwould be very easy to do in a future patch without changing the behavior\nof what we have by now.\n\n"},{"id":"522827","messageId":"20250727175110.84770-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 0/5] repo: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-27T17:51:05Z","receivedAt":"2025-07-27T17:51:40Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\nThis v6 contains small fixes pointed in v5.\n\nThe main changes were:\n\n- The functions that retrieve the values now add a string to a strbuf\n  instead of returning the string\n\n- The null-terminated format is now identified as \"nul\"\n\n- Two test cases were added, covering invalid input values in the CLI\n\nIn v5, Patrick and Junio were discussing about quoting the values in the\nkey=value format using `quote_c_style` [1]. Given that it wouldn't affect\nthis patchset and it's a simple change that can be done later, I didn't\nchange it and I'll leave for further discussion when I start to deal\nwith paths.\n\nThanks!\n\n[1] https://lore.kernel.org/git/aIHRCz_qswp7RgSy@pks.im/\n\nRange-diff versus v5:\n\n1:  396cfc256a ! 1:  bc6f19ba8e repo: declare the repo command\n    @@ Commit message\n\n         Create a new Git command called `repo`. `git repo` will be the main\n         command for obtaining the information about a repository (such as\n    -    metadata and metrics), returning them in a machine readable format\n    -    following the syntax \"field<LF>value<NUL>\".\n    +    metadata and metrics).\n\n         Also declare a subcommand for `repo` called `info`. `git repo info`\n         will bring the functionality of retrieving repository-related\n    @@ Documentation/git-repo.adoc (new)\n     +\n     +COMMANDS\n     +--------\n    -+info [<key>...]::\n    ++`info [<key>...]`::\n     +\tRetrieve metadata-related information about the current repository. Only\n     +\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n     +\tsection below).\n2:  28f4c21a96 ! 2:  b1c0627af8 repo: add the field references.format\n    @@ Commit message\n         used for querying repository metadata, fitting in the purpose of\n         git-repo-info.\n\n    -    Then, add a new field `references.format` to the repo-info subcommand\n    +    Add a new field `references.format` to the repo-info subcommand\n         containing that information.\n\n         Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n    @@ builtin/repo.c\n      #include \"builtin.h\"\n      #include \"parse-options.h\"\n     +#include \"refs.h\"\n    ++#include \"strbuf.h\"\n\n     -static int repo_info(int argc UNUSED, const char **argv UNUSED,\n     -\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n    -+typedef const char *get_value_fn(struct repository *repo);\n    ++typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n     +\n     +struct field {\n     +\tconst char *key;\n    -+\tget_value_fn *add_field_callback;\n    ++\tget_value_fn *get_value;\n     +};\n     +\n    -+static const char *get_references_format(struct repository *repo)\n    ++static int get_references_format(struct repository *repo, struct strbuf *buf)\n     +{\n    -+\treturn ref_storage_format_to_name(repo->ref_storage_format);\n    ++\tstrbuf_addstr(buf,\n    ++\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n    ++\treturn 0;\n     +}\n     +\n     +/* repo_info_fields keys should be in lexicographical order */\n    @@ builtin/repo.c\n     +\treturn strcmp(a->key, b->key);\n     +}\n     +\n    -+static get_value_fn *get_value_callback(const char *key)\n    - {\n    ++static get_value_fn *get_value_fn_for_key(const char *key)\n    ++{\n     +\tconst struct field search_key = { key, NULL };\n     +\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n     +\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n    -+\t\t\t\t\t    sizeof(struct field),\n    ++\t\t\t\t\t    sizeof(*found),\n     +\t\t\t\t\t    repo_info_fields_cmp);\n    -+\treturn found ? found->add_field_callback : NULL;\n    ++\treturn found ? found->get_value : NULL;\n     +}\n     +\n     +static int qsort_strcmp(const void *va, const void *vb)\n    -+{\n    + {\n     +\tconst char *a = *(const char **)va;\n     +\tconst char *b = *(const char **)vb;\n     +\n    @@ builtin/repo.c\n     +\tQSORT(argv, argc, qsort_strcmp);\n     +\n     +\tfor (int i = 0; i < argc; i++) {\n    -+\t\tget_value_fn *callback;\n    ++\t\tget_value_fn *get_value;\n     +\t\tconst char *key = argv[i];\n    -+\t\tconst char *value;\n    ++\t\tstruct strbuf value;\n    ++\t\tstrbuf_init(&value, 64);\n     +\n     +\t\tif (!strcmp(key, last))\n     +\t\t\tcontinue;\n     +\n    -+\t\tcallback = get_value_callback(key);\n    ++\t\tget_value = get_value_fn_for_key(key);\n     +\n    -+\t\tif (!callback)\n    -+\t\t\treturn error(\"key %s not found\", key);\n    ++\t\tif (!get_value)\n    ++\t\t\tdie(_(\"key '%s' not found\"), key);\n     +\n    -+\t\tvalue = callback(repo);\n    -+\t\tprintf(\"%s=%s\\n\", key, value);\n    ++\t\tget_value(repo, &value);\n    ++\t\tprintf(\"%s=%s\\n\", key, value.buf);\n     +\t\tlast = key;\n    ++\t\tstrbuf_release(&value);\n     +\t}\n     +\n      \treturn 0;\n    @@ t/t1900-repo.sh (new)\n     +test_repo_info 'ref format reftable is retrieved correctly' '\n     +\tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n     +\n    ++test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n    ++\ttest_when_finished \"rm -rf expected err\" &&\n    ++\techo \"fatal: key '\\'foo\\'' not found\" >expected &&\n    ++\ttest_must_fail git repo info foo 2>err &&\n    ++\ttest_cmp expected err\n    ++'\n    ++\n     +test_expect_success \"only one value is returned if the same key is requested twice\" '\n     +\ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n     +\techo \"references.format\" >expected_key &&\n3:  b8001ae87e ! 3:  d002401587 repo: add field layout.bare\n    @@ builtin/repo.c\n     +#define USE_THE_REPOSITORY_VARIABLE\n     +\n      #include \"builtin.h\"\n    ++#include \"environment.h\"\n      #include \"parse-options.h\"\n      #include \"refs.h\"\n    -+#include \"environment.h\"\n    -\n    - typedef const char *get_value_fn(struct repository *repo);\n    -\n    + #include \"strbuf.h\"\n     @@ builtin/repo.c: struct field {\n    - \tget_value_fn *add_field_callback;\n    + \tget_value_fn *get_value;\n      };\n\n    -+static const char *get_layout_bare(struct repository *repo UNUSED)\n    ++static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n     +{\n    -+\treturn is_bare_repository() ? \"true\" : \"false\";\n    ++\tstrbuf_addstr(buf,\n    ++\t\t      is_bare_repository() ? \"true\" : \"false\");\n    ++\treturn 0;\n     +}\n     +\n    - static const char *get_references_format(struct repository *repo)\n    + static int get_references_format(struct repository *repo, struct strbuf *buf)\n      {\n    - \treturn ref_storage_format_to_name(repo->ref_storage_format);\n    -@@ builtin/repo.c: static const char *get_references_format(struct repository *repo)\n    + \tstrbuf_addstr(buf,\n    +@@ builtin/repo.c: static int get_references_format(struct repository *repo, struct strbuf *buf)\n\n      /* repo_info_fields keys should be in lexicographical order */\n      static const struct field repo_info_fields[] = {\n    @@ t/t1900-repo.sh: test_repo_info 'ref format files is retrieved correctly' '\n     +test_repo_info 'bare repository = true is retrieved correctly' '\n     +\tgit init --bare repo' 'layout.bare' 'true'\n     +\n    - test_expect_success \"only one value is returned if the same key is requested twice\" '\n    - \ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n    - \techo \"references.format\" >expected_key &&\n    + test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n    + \ttest_when_finished \"rm -rf expected err\" &&\n    + \techo \"fatal: key '\\'foo\\'' not found\" >expected &&\n4:  bceba54e8b ! 4:  6eaac3f9c3 repo: add field layout.shallow\n    @@ builtin/repo.c\n     @@\n      #include \"parse-options.h\"\n      #include \"refs.h\"\n    - #include \"environment.h\"\n    + #include \"strbuf.h\"\n     +#include \"shallow.h\"\n\n    - typedef const char *get_value_fn(struct repository *repo);\n    + typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n\n    -@@ builtin/repo.c: static const char *get_layout_bare(struct repository *repo UNUSED)\n    - \treturn is_bare_repository() ? \"true\" : \"false\";\n    +@@ builtin/repo.c: static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n    + \treturn 0;\n      }\n\n    -+static const char *get_layout_shallow(struct repository *repo)\n    ++static int get_layout_shallow(struct repository *repo, struct strbuf *buf)\n     +{\n    -+\treturn is_repository_shallow(repo) ? \"true\" : \"false\";\n    ++\tstrbuf_addstr(buf,\n    ++\t\t      is_repository_shallow(repo) ? \"true\" : \"false\");\n    ++\treturn 0;\n     +}\n     +\n    - static const char *get_references_format(struct repository *repo)\n    + static int get_references_format(struct repository *repo, struct strbuf *buf)\n      {\n    - \treturn ref_storage_format_to_name(repo->ref_storage_format);\n    -@@ builtin/repo.c: static const char *get_references_format(struct repository *repo)\n    + \tstrbuf_addstr(buf,\n    +@@ builtin/repo.c: static int get_references_format(struct repository *repo, struct strbuf *buf)\n      /* repo_info_fields keys should be in lexicographical order */\n      static const struct field repo_info_fields[] = {\n      \t{ \"layout.bare\", get_layout_bare },\n    @@ t/t1900-repo.sh: test_repo_info 'bare repository = false is retrieved correctly'\n     +\trm -rf remote\n     +\t' 'layout.shallow' 'true'\n     +\n    - test_expect_success \"only one value is returned if the same key is requested twice\" '\n    - \ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n    - \techo \"references.format\" >expected_key &&\n    + test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n    + \ttest_when_finished \"rm -rf expected err\" &&\n    + \techo \"fatal: key '\\'foo\\'' not found\" >expected &&\n     @@ t/t1900-repo.sh: test_expect_success \"only one value is returned if the same key is requested twi\n              test_cmp expected_value actual_value\n      '\n5:  f4a2b0a04e ! 5:  69c7554bf3 repo: add the --format flag\n    @@ Commit message\n         Add the --format flag to git-repo-info. By using this flag, the users\n         can choose the format for obtaining the data they requested.\n\n    -    Given that this command can be used for generating input for another\n    +    Given that this command can be used for generating input for other\n         applications and for being read by end users, it requires at least two\n         formats: one for being read by humans and other for being read by\n         machines. Some other Git commands also have two output formats, notably\n    @@ Documentation/git-repo.adoc: THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHAN\n\n      COMMANDS\n      --------\n    --info [<key>...]::\n    -+info [--format=<format>] [<key>...]::\n    +-`info [<key>...]`::\n    ++`info [--format=<format>] [<key>...]`::\n      \tRetrieve metadata-related information about the current repository. Only\n      \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n      \tsection below).\n    @@ Documentation/git-repo.adoc: THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHAN\n     +* `keyvalue`: output key-value pairs one per line using the `=` character as\n     +the delimiter between the key and the value. This is the default.\n     +\n    -+* `null`: similar to `keyvalue`, but using a newline character as the delimiter\n    ++* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n     +between the key and the value and using a null character after each value.\n     +This format is better suited for being parsed by another applications than\n     +`keyvalue`.\n    @@ Documentation/git-repo.adoc: THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHAN\n      ## builtin/repo.c ##\n     @@\n\n    - typedef const char *get_value_fn(struct repository *repo);\n    + typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n\n     +enum output_format {\n     +\tFORMAT_KEYVALUE,\n    -+\tFORMAT_NULL_TERMINATED,\n    ++\tFORMAT_NUL_TERMINATED,\n     +};\n     +\n      struct field {\n      \tconst char *key;\n    - \tget_value_fn *add_field_callback;\n    + \tget_value_fn *get_value;\n     @@ builtin/repo.c: static int qsort_strcmp(const void *va, const void *vb)\n      \treturn strcmp(a, b);\n      }\n    @@ builtin/repo.c: static int qsort_strcmp(const void *va, const void *vb)\n     +\t\tkv_sep = '=';\n     +\t\tfield_sep = '\\n';\n     +\t\tbreak;\n    -+\tcase FORMAT_NULL_TERMINATED:\n    ++\tcase FORMAT_NUL_TERMINATED:\n     +\t\tkv_sep = '\\n';\n     +\t\tfield_sep = '\\0';\n     +\t\tbreak;\n    @@ builtin/repo.c: static int qsort_strcmp(const void *va, const void *vb)\n      \tQSORT(argv, argc, qsort_strcmp);\n\n     @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repository *repo)\n    - \t\t\treturn error(\"key %s not found\", key);\n    + \t\t\tdie(_(\"key '%s' not found\"), key);\n\n    - \t\tvalue = callback(repo);\n    --\t\tprintf(\"%s=%s\\n\", key, value);\n    -+\t\tprintf(\"%s%c%s%c\", key, kv_sep, value, field_sep);\n    + \t\tget_value(repo, &value);\n    +-\t\tprintf(\"%s=%s\\n\", key, value.buf);\n    ++\t\tprintf(\"%s%c%s%c\", key, kv_sep, value.buf, field_sep);\n      \t\tlast = key;\n    + \t\tstrbuf_release(&value);\n      \t}\n    -\n    +@@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repository *repo)\n      \treturn 0;\n      }\n\n    @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repo\n     +\n     +\tif (!strcmp(format_str, \"keyvalue\"))\n     +\t\tformat = FORMAT_KEYVALUE;\n    -+\telse if (!strcmp(format_str, \"null\"))\n    -+\t\tformat = FORMAT_NULL_TERMINATED;\n    ++\telse if (!strcmp(format_str, \"nul\"))\n    ++\t\tformat = FORMAT_NUL_TERMINATED;\n     +\telse\n    -+\t\tdie(\"invalid format %s\", format_str);\n    ++\t\tdie(_(\"invalid format '%s'\"), format_str);\n     +\n     +\treturn print_fields(argc, argv, repo, format);\n      }\n    @@ t/t1900-repo.sh: test_repo_info () {\n     +\t\ttest_when_finished \"rm -rf repo\" &&\n     +\t\teval \"$init_command\" &&\n     +\t\techo \"$expected_value\" | lf_to_nul >expected &&\n    -+\t\tgit -C repo repo info --format=null \"$key\" >output &&\n    ++\t\tgit -C repo repo info --format=nul \"$key\" >output &&\n     +\t\ttail -n 1 output >actual &&\n     +\t\ttest_cmp expected actual\n     +\t'\n    @@ t/t1900-repo.sh: test_repo_info () {\n      \t\tcut -d \"=\" -f 2 <output >actual &&\n      \t\ttest_cmp expected actual\n      \t'\n    +@@ t/t1900-repo.sh: test_expect_success 'output is returned correctly when two keys are requested' '\n    + \ttest_cmp expect actual\n    + '\n    +\n    ++test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n    ++\ttest_when_finished \"rm -f err expected\" &&\n    ++\techo \"fatal: invalid format '\\'foo\\''\" >expected &&\n    ++\ttest_must_fail git repo info --format=foo 2>err &&\n    ++\ttest_cmp expected err\n    ++'\n    ++\n    + test_done\n\nLucas Seiki Oshiro (5):\n  repo: declare the repo command\n  repo: add the field references.format\n  repo: add field layout.bare\n  repo: add field layout.shallow\n  repo: add the --format flag\n\n .gitignore                  |   1 +\n Documentation/git-repo.adoc |  58 +++++++++++++\n Documentation/meson.build   |   1 +\n Makefile                    |   1 +\n builtin.h                   |   1 +\n builtin/repo.c              | 165 ++++++++++++++++++++++++++++++++++++\n command-list.txt            |   1 +\n git.c                       |   1 +\n meson.build                 |   1 +\n t/meson.build               |   1 +\n t/t1900-repo.sh             | 100 ++++++++++++++++++++++\n 11 files changed, 331 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n create mode 100755 t/t1900-repo.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522828","messageId":"20250727175110.84770-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-27T17:51:06Z","receivedAt":"2025-07-27T17:51:44Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, `git rev-parse` covers a wide range of functionality not\ndirectly related to parsing revisions, as its name suggests. Over time,\nmany features like parsing datestrings, options, paths, and others\nwere added to it because there wasn't a more appropriate command\nto place them.\n\nCreate a new Git command called `repo`. `git repo` will be the main\ncommand for obtaining the information about a repository (such as\nmetadata and metrics).\n\nAlso declare a subcommand for `repo` called `info`. `git repo info`\nwill bring the functionality of retrieving repository-related\ninformation currently returned by `rev-parse`.\n\nAdd the required tests, documentation and build changes to enable\nusage of this subcommand.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore                  |  1 +\n Documentation/git-repo.adoc | 38 +++++++++++++++++++++++++++++++++++++\n Documentation/meson.build   |  1 +\n Makefile                    |  1 +\n builtin.h                   |  1 +\n builtin/repo.c              | 26 +++++++++++++++++++++++++\n command-list.txt            |  1 +\n git.c                       |  1 +\n meson.build                 |  1 +\n 9 files changed, 71 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..1803023427 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nnew file mode 100644\nindex 0000000000..aca76b131b\n--- /dev/null\n+++ b/Documentation/git-repo.adoc\n@@ -0,0 +1,38 @@\n+git-repo(1)\n+===========\n+\n+NAME\n+----\n+git-repo - Retrieve information about a repository\n+\n+SYNOPSIS\n+--------\n+[synopsis]\n+git repo info [<key>...]\n+\n+DESCRIPTION\n+-----------\n+This command retrieve repository level information.\n+\n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n+COMMANDS\n+--------\n+`info [<key>...]`::\n+\tRetrieve metadata-related information about the current repository. Only\n+\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n+\tsection below).\n+\n+INFO KEYS\n+---------\n+\n+The set of data that `git repo` can return is grouped into the following\n+categories:\n+\n+SEE ALSO\n+--------\n+linkgit:git-rev-parse[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 4404c623f0..41f43e0336 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -116,6 +116,7 @@ manpages = {\n   'git-repack.adoc' : 1,\n   'git-replace.adoc' : 1,\n   'git-replay.adoc' : 1,\n+  'git-repo.adoc' : 1,\n   'git-request-pull.adoc' : 1,\n   'git-rerere.adoc' : 1,\n   'git-reset.adoc' : 1,\ndiff --git a/Makefile b/Makefile\nindex 5f7dd79dfa..9dce446309 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1306,6 +1306,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..e6458e6fb9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo.c b/builtin/repo.c\nnew file mode 100644\nindex 0000000000..d4f01e35e2\n--- /dev/null\n+++ b/builtin/repo.c\n@@ -0,0 +1,26 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+static int repo_info(int argc UNUSED, const char **argv UNUSED,\n+\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+{\n+\treturn 0;\n+}\n+\n+int cmd_repo(int argc, const char **argv, const char *prefix,\n+\t     struct repository *repo)\n+{\n+\tparse_opt_subcommand_fn *fn = NULL;\n+\tconst char *const repo_usage[] = {\n+\t\t\"git repo info [<key>...]\",\n+\t\tNULL\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\treturn fn(argc, argv, prefix, repo);\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex b7ade3ab9f..1b0bdee00d 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -164,6 +164,7 @@ git-remote                              ancillarymanipulators           complete\n git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-replay                              plumbingmanipulators\n+git-repo                                plumbinginterrogators\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           history\ndiff --git a/git.c b/git.c\nindex 07a5fe39fb..8290d8b8c8 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo\", cmd_repo, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 9bc1826cb6..8819b64f93 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522829","messageId":"20250727175110.84770-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-27T17:51:07Z","receivedAt":"2025-07-27T17:51:48Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--show-ref-format` from git-rev-parse is used for retrieving\nthe reference format (i.e. `files` or `reftable`). This way, it is\nused for querying repository metadata, fitting in the purpose of\ngit-repo-info.\n\nAdd a new field `references.format` to the repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  4 ++\n builtin/repo.c              | 82 ++++++++++++++++++++++++++++++++++++-\n t/meson.build               |  1 +\n t/t1900-repo.sh             | 57 ++++++++++++++++++++++++++\n 4 files changed, 142 insertions(+), 2 deletions(-)\n create mode 100755 t/t1900-repo.sh\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex aca76b131b..ac2578299f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -29,6 +29,10 @@ INFO KEYS\n The set of data that `git repo` can return is grouped into the following\n categories:\n \n+`references`::\n+Reference-related data:\n+* `format`: the reference storage format\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex d4f01e35e2..02d5821c77 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,12 +1,90 @@\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"refs.h\"\n+#include \"strbuf.h\"\n \n-static int repo_info(int argc UNUSED, const char **argv UNUSED,\n-\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n+\n+struct field {\n+\tconst char *key;\n+\tget_value_fn *get_value;\n+};\n+\n+static int get_references_format(struct repository *repo, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf,\n+\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n+\treturn 0;\n+}\n+\n+/* repo_info_fields keys should be in lexicographical order */\n+static const struct field repo_info_fields[] = {\n+\t{ \"references.format\", get_references_format },\n+};\n+\n+static int repo_info_fields_cmp(const void *va, const void *vb)\n+{\n+\tconst struct field *a = va;\n+\tconst struct field *b = vb;\n+\n+\treturn strcmp(a->key, b->key);\n+}\n+\n+static get_value_fn *get_value_fn_for_key(const char *key)\n+{\n+\tconst struct field search_key = { key, NULL };\n+\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n+\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n+\t\t\t\t\t    sizeof(*found),\n+\t\t\t\t\t    repo_info_fields_cmp);\n+\treturn found ? found->get_value : NULL;\n+}\n+\n+static int qsort_strcmp(const void *va, const void *vb)\n {\n+\tconst char *a = *(const char **)va;\n+\tconst char *b = *(const char **)vb;\n+\n+\treturn strcmp(a, b);\n+}\n+\n+static int print_fields(int argc, const char **argv, struct repository *repo)\n+{\n+\tconst char *last = \"\";\n+\n+\tQSORT(argv, argc, qsort_strcmp);\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tget_value_fn *get_value;\n+\t\tconst char *key = argv[i];\n+\t\tstruct strbuf value;\n+\n+\t\tif (!strcmp(key, last))\n+\t\t\tcontinue;\n+\n+\t\tstrbuf_init(&value, 64);\n+\t\tget_value = get_value_fn_for_key(key);\n+\n+\t\tif (!get_value) {\n+\t\t\tstrbuf_release(&value);\n+\t\t\treturn error(_(\"key '%s' not found\"), key);\n+\t\t}\n+\n+\t\tget_value(repo, &value);\n+\t\tprintf(\"%s=%s\\n\", key, value.buf);\n+\t\tlast = key;\n+\t\tstrbuf_release(&value);\n+\t}\n+\n \treturn 0;\n }\n \n+static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+\t\t     struct repository *repo)\n+{\n+\treturn print_fields(argc - 1, argv + 1, repo);\n+}\n+\n int cmd_repo(int argc, const char **argv, const char *prefix,\n \t     struct repository *repo)\n {\ndiff --git a/t/meson.build b/t/meson.build\nindex 660d780dcc..5de9c3c7e9 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -245,6 +245,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nnew file mode 100755\nindex 0000000000..cc54b0644d\n--- /dev/null\n+++ b/t/t1900-repo.sh\n@@ -0,0 +1,57 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+\n+. ./test-lib.sh\n+\n+# Test if a field is correctly returned in the null-terminated format\n+#\n+# Usage: test_repo_info <label> <init command> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init command: a command that creates a repository called 'repo', configured\n+#      accordingly to what is being tested\n+#   key: the key of the field that is being tested\n+#   expected value: the value that the field should contain\n+test_repo_info () {\n+\tlabel=$1\n+\tinit_command=$2\n+\tkey=$3\n+\texpected_value=$4\n+\n+\ttest_expect_success \"$label\" '\n+\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\teval \"$init_command\" &&\n+\t\techo \"$expected_value\" >expected &&\n+\t\tgit -C repo repo info \"$key\" >output &&\n+\t\tcut -d \"=\" -f 2 <output >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n+\n+test_repo_info 'ref format files is retrieved correctly' '\n+\tgit init --ref-format=files repo' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' '\n+\tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n+\n+test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n+\ttest_when_finished \"rm -rf expected err\" &&\n+\techo \"error: key '\\'foo\\'' not found\" >expected &&\n+\ttest_must_fail git repo info foo 2>err &&\n+\ttest_cmp expected err\n+'\n+\n+test_expect_success \"only one value is returned if the same key is requested twice\" '\n+\ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n+\techo \"references.format\" >expected_key &&\n+\tgit rev-parse --show-ref-format >expected_value &&\n+\tgit repo info references.format references.format >output &&\n+\tcut -d \"=\" -f 1 <output >actual_key &&\n+\tcut -d \"=\" -f 2 <output >actual_value &&\n+        test_cmp expected_key actual_key &&\n+        test_cmp expected_value actual_value\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522830","messageId":"20250727175110.84770-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 3/5] repo: add field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-27T17:51:08Z","receivedAt":"2025-07-27T17:51:52Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag --is-bare-repository from git-rev-parse is used for retrieving\nwhether the current repository is bare. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field layout.bare to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  4 ++++\n builtin/repo.c              | 11 +++++++++++\n t/t1900-repo.sh             |  6 ++++++\n 3 files changed, 21 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex ac2578299f..d52f4666be 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -33,6 +33,10 @@ categories:\n Reference-related data:\n * `format`: the reference storage format\n \n+`layout`::\n+Information about the how the current repository is represented:\n+* `bare`: `true` if this is a bare repository, otherwise `false`.\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 02d5821c77..2aba6a39c7 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,4 +1,7 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n+#include \"environment.h\"\n #include \"parse-options.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n@@ -10,6 +13,13 @@ struct field {\n \tget_value_fn *get_value;\n };\n \n+static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf,\n+\t\t      is_bare_repository() ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -19,6 +29,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n \n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n+\t{ \"layout.bare\", get_layout_bare },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex cc54b0644d..7304629cb2 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -36,6 +36,12 @@ test_repo_info 'ref format files is retrieved correctly' '\n test_repo_info 'ref format reftable is retrieved correctly' '\n \tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' '\n+\tgit init --bare repo' 'layout.bare' 'true'\n+\n test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n \ttest_when_finished \"rm -rf expected err\" &&\n \techo \"error: key '\\'foo\\'' not found\" >expected &&\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522831","messageId":"20250727175110.84770-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 4/5] repo: add field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-27T17:51:09Z","receivedAt":"2025-07-27T17:51:56Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--is-shallow-repository` from git-rev-parse is used for\nretrieving whether the repository is shallow. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field `layout.shallow` to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  1 +\n builtin/repo.c              |  9 +++++++++\n t/t1900-repo.sh             | 21 +++++++++++++++++++++\n 3 files changed, 31 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex d52f4666be..3f920b619f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -36,6 +36,7 @@ Reference-related data:\n `layout`::\n Information about the how the current repository is represented:\n * `bare`: `true` if this is a bare repository, otherwise `false`.\n+* `shallow`: `true` if this is a shallow repository, otherwise `false`.\n \n SEE ALSO\n --------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 2aba6a39c7..37fb1803f6 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -5,6 +5,7 @@\n #include \"parse-options.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n+#include \"shallow.h\"\n \n typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n \n@@ -20,6 +21,13 @@ static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_layout_shallow(struct repository *repo, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf,\n+\t\t      is_repository_shallow(repo) ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -30,6 +38,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n \t{ \"layout.bare\", get_layout_bare },\n+\t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 7304629cb2..0bdbf6911e 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -42,6 +42,20 @@ test_repo_info 'bare repository = false is retrieved correctly' '\n test_repo_info 'bare repository = true is retrieved correctly' '\n \tgit init --bare repo' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' '\n+\tgit init repo' 'layout.shallow' 'false'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' '\n+\tgit init remote &&\n+\tcd remote &&\n+\techo x >x &&\n+\tgit add x &&\n+\tgit commit -m x &&\n+\tcd .. &&\n+\tgit clone --depth 1 \"file://$PWD/remote\" repo &&\n+\trm -rf remote\n+\t' 'layout.shallow' 'true'\n+\n test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n \ttest_when_finished \"rm -rf expected err\" &&\n \techo \"error: key '\\'foo\\'' not found\" >expected &&\n@@ -60,4 +74,11 @@ test_expect_success \"only one value is returned if the same key is requested twi\n         test_cmp expected_value actual_value\n '\n \n+test_expect_success 'output is returned correctly when two keys are requested' '\n+\ttest_when_finished \"rm -f expect\" &&\n+\tprintf \"layout.bare=false\\nlayout.shallow=false\\n\" >expect &&\n+\tgit repo info layout.shallow layout.bare >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522832","messageId":"20250727175110.84770-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-27T17:51:10Z","receivedAt":"2025-07-27T17:52:00Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the --format flag to git-repo-info. By using this flag, the users\ncan choose the format for obtaining the data they requested.\n\nGiven that this command can be used for generating input for other\napplications and for being read by end users, it requires at least two\nformats: one for being read by humans and other for being read by\nmachines. Some other Git commands also have two output formats, notably\ngit-config which was the inspiration for the two formats that were\nchosen here:\n\n- keyvalue, where the retrieved data is printed one per line, using =\n  for delimiting the key and the value. This is the default format,\n  targeted for end users.\n- nul, where the retrieved data is separated by null characters, using\n  the newline character for delimiting the key and the value. This\n  format is targeted for being read by machines.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 13 +++++++++-\n builtin/repo.c              | 49 ++++++++++++++++++++++++++++++++++---\n t/t1900-repo.sh             | 20 +++++++++++++--\n 3 files changed, 75 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 3f920b619f..6cc3ff581c 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -18,10 +18,21 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n \n COMMANDS\n --------\n-`info [<key>...]`::\n+`info [--format=<format>] [<key>...]`::\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n++\n+The output format can be chosen through the flag `--format`. Two formats are\n+supported:\n++\n+* `keyvalue`: output key-value pairs one per line using the `=` character as\n+the delimiter between the key and the value. This is the default.\n+\n+* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n+between the key and the value and using a null character after each value.\n+This format is better suited for being parsed by another applications than\n+`keyvalue`.\n \n INFO KEYS\n ---------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 37fb1803f6..a293d32b92 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -9,6 +9,11 @@\n \n typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n \n+enum output_format {\n+\tFORMAT_KEYVALUE,\n+\tFORMAT_NUL_TERMINATED,\n+};\n+\n struct field {\n \tconst char *key;\n \tget_value_fn *get_value;\n@@ -68,9 +73,24 @@ static int qsort_strcmp(const void *va, const void *vb)\n \treturn strcmp(a, b);\n }\n \n-static int print_fields(int argc, const char **argv, struct repository *repo)\n+static int print_fields(int argc, const char **argv,\n+\t\t\tstruct repository *repo,\n+\t\t\tenum output_format format)\n {\n \tconst char *last = \"\";\n+\tchar kv_sep;\n+\tchar field_sep;\n+\n+\tswitch (format) {\n+\tcase FORMAT_KEYVALUE:\n+\t\tkv_sep = '=';\n+\t\tfield_sep = '\\n';\n+\t\tbreak;\n+\tcase FORMAT_NUL_TERMINATED:\n+\t\tkv_sep = '\\n';\n+\t\tfield_sep = '\\0';\n+\t\tbreak;\n+\t}\n \n \tQSORT(argv, argc, qsort_strcmp);\n \n@@ -91,7 +111,7 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \t\t}\n \n \t\tget_value(repo, &value);\n-\t\tprintf(\"%s=%s\\n\", key, value.buf);\n+\t\tprintf(\"%s%c%s%c\", key, kv_sep, value.buf, field_sep);\n \t\tlast = key;\n \t\tstrbuf_release(&value);\n \t}\n@@ -99,10 +119,31 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \treturn 0;\n }\n \n-static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+static int repo_info(int argc, const char **argv, const char *prefix,\n \t\t     struct repository *repo)\n {\n-\treturn print_fields(argc - 1, argv + 1, repo);\n+\tconst char *format_str = \"keyvalue\";\n+\tenum output_format format;\n+\tconst char *const repo_info_usage[] = {\n+\t\t\"git repo info [<key>...]\",\n+\t\tNULL\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n+\t\t\t   N_(\"output format\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_info_usage, 0);\n+\n+\tif (!strcmp(format_str, \"keyvalue\"))\n+\t\tformat = FORMAT_KEYVALUE;\n+\telse if (!strcmp(format_str, \"nul\"))\n+\t\tformat = FORMAT_NUL_TERMINATED;\n+\telse\n+\t\tdie(_(\"invalid format '%s'\"), format_str);\n+\n+\treturn print_fields(argc, argv, repo, format);\n }\n \n int cmd_repo(int argc, const char **argv, const char *prefix,\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 0bdbf6911e..94a98c52f6 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -20,11 +20,20 @@ test_repo_info () {\n \tkey=$3\n \texpected_value=$4\n \n-\ttest_expect_success \"$label\" '\n+\ttest_expect_success \"null-terminated: $label\" '\n+\t\ttest_when_finished \"rm -rf repo\" &&\n+\t\teval \"$init_command\" &&\n+\t\techo \"$expected_value\" | lf_to_nul >expected &&\n+\t\tgit -C repo repo info --format=nul \"$key\" >output &&\n+\t\ttail -n 1 output >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+\n+\ttest_expect_success \"key-value: $label\" '\n \t\ttest_when_finished \"rm -rf repo\" &&\n \t\teval \"$init_command\" &&\n \t\techo \"$expected_value\" >expected &&\n-\t\tgit -C repo repo info \"$key\" >output &&\n+\t\tgit -C repo repo info --format=keyvalue \"$key\" >output &&\n \t\tcut -d \"=\" -f 2 <output >actual &&\n \t\ttest_cmp expected actual\n \t'\n@@ -81,4 +90,11 @@ test_expect_success 'output is returned correctly when two keys are requested' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n+\ttest_when_finished \"rm -f err expected\" &&\n+\techo \"fatal: invalid format '\\'foo\\''\" >expected &&\n+\ttest_must_fail git repo info --format=foo 2>err &&\n+\ttest_cmp expected err\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"522833","messageId":"CAPig+cSBj+kSmzKUarNPQt4qk5p9vjHFGFrJVYmT6TrWHACcEg@mail.gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 0/5] repo: add new command for retrieving repository info","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-07-27T20:11:57Z","receivedAt":"2025-07-27T20:12:09Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Jul 27, 2025 at 1:51 PM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> This v6 contains small fixes pointed in v5.\n>\n> In v5, Patrick and Junio were discussing about quoting the values in the\n> key=value format using `quote_c_style` [1]. Given that it wouldn't affect\n> this patchset and it's a simple change that can be done later, I didn't\n> change it and I'll leave for further discussion when I start to deal\n> with paths.\n\nThe counterargument to this stance is that if you employ\n`quote_c_style` from the onset and document it, then if a future\nversion of Git does start outputting values containing \"funny\"\ncharacters for properties which previously did not emit such values\n(this isn't limited to paths), then consumers who heeded the\ndocumentation won't find their tooling breaking suddenly. Tooling may\nbreak for consumers who did not heed the documentation, but that will\nbe due to their own shortsightedness, not due to the Git project\nfailing to care about compatibility between versions. For this reason,\nI'm of the opinion that `quote_c_style` should be used and documented\neven at this very early stage.\n"},{"id":"522834","messageId":"CAPig+cRwv1xOoiXRBo0tECXPKV=BrHo_aHcD6Wy+mOUv4OvuJA@mail.gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-2-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-07-27T20:20:31Z","receivedAt":"2025-07-27T20:20:43Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Jul 27, 2025 at 1:51 PM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> Currently, `git rev-parse` covers a wide range of functionality not\n> directly related to parsing revisions, as its name suggests. Over time,\n> many features like parsing datestrings, options, paths, and others\n> were added to it because there wasn't a more appropriate command\n> to place them.\n>\n> Create a new Git command called `repo`. `git repo` will be the main\n> command for obtaining the information about a repository (such as\n> metadata and metrics).\n>\n> Also declare a subcommand for `repo` called `info`. `git repo info`\n> will bring the functionality of retrieving repository-related\n> information currently returned by `rev-parse`.\n>\n> Add the required tests, documentation and build changes to enable\n> usage of this subcommand.\n\nThis talks about adding tests, however...\n\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  .gitignore                  |  1 +\n>  Documentation/git-repo.adoc | 38 +++++++++++++++++++++++++++++++++++++\n>  Documentation/meson.build   |  1 +\n>  Makefile                    |  1 +\n>  builtin.h                   |  1 +\n>  builtin/repo.c              | 26 +++++++++++++++++++++++++\n>  command-list.txt            |  1 +\n>  git.c                       |  1 +\n>  meson.build                 |  1 +\n>  9 files changed, 71 insertions(+)\n\n...no tests are added.\n\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> @@ -0,0 +1,38 @@\n> +DESCRIPTION\n> +-----------\n> +This command retrieve repository level information.\n\ns/retrieve/retrieves/\n\nI'm a native English speaker, but I wonder if, rather than \"repository\nlevel\", it would be more friendly to non-native speakers to instead\nsay:\n\n    Retrieve information about the repository.\n"},{"id":"522835","messageId":"CAPig+cTuiUy=+2Jf1Lrp1gaM03_zPf8EFMVSKmShqU05t-3aWQ@mail.gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-07-27T21:16:59Z","receivedAt":"2025-07-27T21:17:11Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Jul 27, 2025 at 1:52 PM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> This commit is part of the series that introduces the new subcommand\n> git-repo-info.\n>\n> The flag `--show-ref-format` from git-rev-parse is used for retrieving\n> the reference format (i.e. `files` or `reftable`). This way, it is\n> used for querying repository metadata, fitting in the purpose of\n> git-repo-info.\n>\n> Add a new field `references.format` to the repo-info subcommand\n> containing that information.\n>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> @@ -29,6 +29,10 @@ INFO KEYS\n>  The set of data that `git repo` can return is grouped into the following\n>  categories:\n>\n> +`references`::\n> +Reference-related data:\n> +* `format`: the reference storage format\n\nBased upon the implementation, I can see that the user must type the\nkey in \"dotted\" form:\n\n    git repo info references.format\n\nbut I wonder if the above documentation actually conveys this\nrequirement. I don't think I would figure it out easily. Perhaps\nhand-holding the user by giving an example would help.\n\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> +/* repo_info_fields keys should be in lexicographical order */\n> +static const struct field repo_info_fields[] = {\n> +       { \"references.format\", get_references_format },\n> +};\n\nHow can we ensure that the lexicographical-order requirement won't\nbreak? If someone adds a new entry which is not in its proper place,\npresumably that will be noticed because some existing test in a test\nscript will stop working, but it feels unnecessarily fragile and a bit\nof a maintenance burden. Also, this requirement does feel like a\npremature optimization. Do you expect this list to become so huge and\nthe corresponding lookup function to be called so frequently that a\nsimple brute-force linear search would be too slow?\n\n> +static int qsort_strcmp(const void *va, const void *vb)\n>  {\n> +       const char *a = *(const char **)va;\n> +       const char *b = *(const char **)vb;\n> +\n> +       return strcmp(a, b);\n> +}\n> +\n> +static int print_fields(int argc, const char **argv, struct repository *repo)\n> +{\n> +       const char *last = \"\";\n> +\n> +       QSORT(argv, argc, qsort_strcmp);\n\nI can see from the implementation that you are sorting the incoming\narguments in order to detect and fold out duplicates. However, that\nraises a couple questions. First, is it really a good idea to do\nsomething other than what the user asked for? Second, if this is a\ngood idea, then should the behavior be documented?\n\nI can see arguments in favor of sorting and de-duplicating, as well as\nin favor of producing exactly the (unordered) output the user asked\nfor, including duplicates, so I don't have a strong opinion either\nway. But, if you do retain this behavior, then the sorting and\ndeduplication behaviors should probably be documented.\n\n> +       for (int i = 0; i < argc; i++) {\n> +               get_value_fn *get_value;\n> +               const char *key = argv[i];\n> +               struct strbuf value;\n> +\n> +               if (!strcmp(key, last))\n> +                       continue;\n> +\n> +               strbuf_init(&value, 64);\n> +               get_value = get_value_fn_for_key(key);\n> +\n> +               if (!get_value) {\n> +                       strbuf_release(&value);\n> +                       return error(_(\"key '%s' not found\"), key);\n> +               }\n> +\n> +               get_value(repo, &value);\n\nA couple observations:\n\nFirst, you don't actually use the strbuf until the call to\nget_value(), so the strbuf_init() call seems to be too early, with the\nresult that you need a corresponding strbuf_release() in the error\nbranch. If you move the strbuf_init() so it occurs immediately before\nget_value(), then you can simplify the early exit case.\n\nSecond, this seems to be getting unnecessarily intimate with strbuf. I\ncan guess that you're doing this late strbuf_init() to avoid an\nallocation in the case when a duplicate key was encountered, however,\nSTRBUF_INIT doesn't actually perform an allocation, so it would be\nclearer to just initialize the strbuf at the time you declare it\nrather than calling strbuf_init() manually. However...\n\nAlthough it is a micro optimization (as well as a\npremature-optimization), it is far more common in this code base to\nhoist the strbuf outside of the loop and instead call strbuf_reset()\nit upon each iteration:\n\n    struct strbuf value = STRBUF_INIT;\n    for (...) {\n        strbuf_reset(&value);\n        ...\n        if (error_condition) {\n            strbuf_release(...);\n            return error(...);\n        }\n       ...\n    }\n    strbuf_release(...);\n\n> +               printf(\"%s=%s\\n\", key, value.buf);\n> +               last = key;\n> +               strbuf_release(&value);\n> +       }\n> +\n>         return 0;\n>  }\n\nLooking at this from a higher level, is it presenting a good\nuser-experience by potentially printing some output but then erroring\nout upon the first unrecognized key? Would the user-experience be\nimproved by instead continuing the loop even after reporting an error,\nand then adjusting the final `return 0` to conditionally return\nsuccess or error depending upon whether any keys were unrecognized?\n\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> @@ -0,0 +1,57 @@\n> +#!/bin/sh\n> +\n> +test_description='test git repo-info'\n> +\n> +. ./test-lib.sh\n> +\n> +# Test if a field is correctly returned in the null-terminated format\n\nThis is talking about null-terminated format, but the implementation\ndoesn't seem to emit NUL-terminated output at all.\n\n> +# Usage: test_repo_info <label> <init command> <key> <expected value>\n> +#\n> +# Arguments:\n> +#   label: the label of the test\n> +#   init command: a command that creates a repository called 'repo', configured\n> +#      accordingly to what is being tested\n> +#   key: the key of the field that is being tested\n> +#   expected value: the value that the field should contain\n> +test_repo_info () {\n> +       label=$1\n> +       init_command=$2\n> +       key=$3\n> +       expected_value=$4\n> +\n> +       test_expect_success \"$label\" '\n> +               test_when_finished \"rm -rf repo\" &&\n> +               eval \"$init_command\" &&\n> +               echo \"$expected_value\" >expected &&\n> +               git -C repo repo info \"$key\" >output &&\n> +               cut -d \"=\" -f 2 <output >actual &&\n> +               test_cmp expected actual\n> +       '\n> +}\n\nIt seems that this could be simplified by crafting the expected output\nmore precisely?\n\n    eval ... &&\n    echo \"$key=$expected_value\" >expect &&\n    git -C repo repo info \"$key\" >actual &&\n    test_cmp expect actual\n\nBy the way, we typically avoid cleaning up test detritus merely for\nthe sake of cleaning up because doing so slows down the already\ntoo-slow test suite. Instead, cleanup is usually only performed when\nabsolutely necessary to avoid some undesirable interaction between\ntests. Avoiding cleanup also makes it easier to debug failed tests\nsince (hopefully) the cause of the failure is still present in the\n\"trash\" directory.\n\nIn this case, if you call this function with a distinct repository\nname each time, then you don't have to remove the repository at all.\nMoreover, giving each repository a distinct and _meaningful_ name,\nrather than reusing the same name, could also be helpful when\ndiagnosing failures.\n\n> +test_repo_info 'ref format files is retrieved correctly' '\n> +       git init --ref-format=files repo' 'references.format' 'files'\n> +\n> +test_repo_info 'ref format reftable is retrieved correctly' '\n> +       git init --ref-format=reftable repo' 'references.format' 'reftable'\n\nThis is overly fragile. The `test_repo_info` function hardcodes the\nname \"repo\" but then the caller is also expected to pass in the name\nas part of the initialization argument (i.e. `git init ... repo`). To\nmake this more robust, either don't hardcode it in the function, or\nstop expecting the caller to supply the name as part of the\ninitialization argument.\n\nWith only two callers, it's not clear at this point whether the\n`test_repo_info` function is providing any added value, especially\nsince the additional abstraction increases cognitive load, but perhaps\nlater patches in this series add more callers?\n\n> +test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n> +       test_when_finished \"rm -rf expected err\" &&\n> +       echo \"error: key '\\'foo\\'' not found\" >expected &&\n> +       test_must_fail git repo info foo 2>err &&\n> +       test_cmp expected err\n> +'\n> +\n> +test_expect_success \"only one value is returned if the same key is requested twice\" '\n\nThis test title can be single-quoted rather than double-quoted.\n\n> +       test_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n> +       echo \"references.format\" >expected_key &&\n> +       git rev-parse --show-ref-format >expected_value &&\n> +       git repo info references.format references.format >output &&\n> +       cut -d \"=\" -f 1 <output >actual_key &&\n> +       cut -d \"=\" -f 2 <output >actual_value &&\n> +        test_cmp expected_key actual_key &&\n> +        test_cmp expected_value actual_value\n> +'\n\nAs above, it seems that this could be simplified by crafting the\nexpected state more precisely rather than slicing and dicing the\nactual output. Perhaps something like this?\n\n    val=$(git rev-parse --show-ref-format) &&\n    echo \"references.format=$val\" >expect &&\n    git repo info references.format references.format >actual &&\n    test_cmp expect actual\n\nAside from the above tests, based upon the implementation, I also\nexpected to find a test checking that the command correctly outputs\nmultiple values, but perhaps a later patch adds that since, presently,\nthe implementation only knows \"references.format\" (thus with\ndeduplication, you can't yet implement such a test).\n"},{"id":"522836","messageId":"CAPig+cRyRZma-b4wdPrhHnRrOh8AESDV-Bt1oxZgncddfEqv4w@mail.gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-5-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 4/5] repo: add field layout.shallow","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-07-27T21:45:46Z","receivedAt":"2025-07-27T21:45:58Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Jul 27, 2025 at 1:52 PM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> This commit is part of the series that introduces the new subcommand\n> git-repo-info.\n>\n> The flag `--is-shallow-repository` from git-rev-parse is used for\n> retrieving whether the repository is shallow. This way, it is used for\n> querying repository metadata, fitting in the purpose of git-repo-info.\n>\n> Then, add a new field `layout.shallow` to the git-repo-info subcommand\n> containing that information.\n>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> @@ -42,6 +42,20 @@ test_repo_info 'bare repository = false is retrieved correctly' '\n> +test_repo_info 'shallow repository = true is retrieved correctly' '\n> +       git init remote &&\n> +       cd remote &&\n> +       echo x >x &&\n> +       git add x &&\n> +       git commit -m x &&\n> +       cd .. &&\n> +       git clone --depth 1 \"file://$PWD/remote\" repo &&\n> +       rm -rf remote\n> +       ' 'layout.shallow' 'true'\n\nIf a command fails between the `cd remote` and the `cd ..`, then the\ntest will abort while the working directory is still \"remote\", and\nevery subsequent test will then run in the wrong directory (because\nthe tests are not isolated from one another in that way). So, the rule\nis: never use `cd` outside of a subshell. Therefore, you want to do\nsomething like this:\n\n    git init remote &&\n    (\n        cd remote &&\n        echo x >x &&\n        git add x &&\n        git commit -m x\n    )\n    git clone --depth 1 \"file://$PWD/remote\" repo &&\n    rm -rf remote\n\nAlternatively, you could avoid `cd` entirely:\n\n    git init remote &&\n    echo x >remote/x &&\n    git -C remote add x &&\n    git -C remote commit -m x &&\n    git clone --depth 1 \"file://$PWD/remote\" repo &&\n    rm -rf remote\n\nThe choice is subjective, though I find that I can spot the secondary\nrepository more easily in the first example.\n\nAs I noted in my review of an earlier patch in this series, we don't\nusually clean up just for the sake of cleaning up since doing so makes\nit more difficult to diagnose a failed test and slows down the test\nsuite, so you could probably also drop the `rm -rf remote.\n\n> @@ -60,4 +74,11 @@ test_expect_success \"only one value is returned if the same key is requested twi\n> +test_expect_success 'output is returned correctly when two keys are requested' '\n\nI think this test could have been added in the previous patch (and\nit's where I was expecting to find it).\n\n> +       test_when_finished \"rm -f expect\" &&\n> +       printf \"layout.bare=false\\nlayout.shallow=false\\n\" >expect &&\n\nThis could be made a bit easier to read either like this:\n\n    test_write_lines layout.bare=false layout.shallow=false >expect &&\n\nor like this:\n\n    cat >expect <<-\\EOF\n    layout.bare=false\n    layout.shallow=false\n    EOF\n\nIt's subjective, but I find the latter example to be more obvious at a glance.\n\n> +       git repo info layout.shallow layout.bare >actual &&\n> +       test_cmp expect actual\n> +'\n"},{"id":"522837","messageId":"CAPig+cQn7c5+k06yHOD2jxYTGnny7is=fbo4tOw26eD+4zX-Jw@mail.gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-07-27T22:02:09Z","receivedAt":"2025-07-27T22:02:21Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Jul 27, 2025 at 2:02 PM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> Add the --format flag to git-repo-info. By using this flag, the users\n> can choose the format for obtaining the data they requested.\n> [...]\n> - keyvalue, where the retrieved data is printed one per line, using =\n>   for delimiting the key and the value. This is the default format,\n>   targeted for end users.\n> - nul, where the retrieved data is separated by null characters, using\n>   the newline character for delimiting the key and the value. This\n>   format is targeted for being read by machines.\n>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> @@ -18,10 +18,21 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n> +`info [--format=<format>] [<key>...]`::\n> ++\n> +The output format can be chosen through the flag `--format`. Two formats are\n> +supported:\n> ++\n> +* `keyvalue`: output key-value pairs one per line using the `=` character as\n> +the delimiter between the key and the value. This is the default.\n> +\n> +* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n> +between the key and the value and using a null character after each value.\n> +This format is better suited for being parsed by another applications than\n> +`keyvalue`.\n\ns/another/other/\n\nI haven't been following the discussion around this series, but don't\nwe also usually provide a `-z` short option? Should that be added for\nconsistency with other commands and to avoid surprising users, or is\nit too early to commit to that?\n\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> @@ -20,11 +20,20 @@ test_repo_info () {\n> +       test_expect_success \"null-terminated: $label\" '\n> +               test_when_finished \"rm -rf repo\" &&\n> +               eval \"$init_command\" &&\n> +               echo \"$expected_value\" | lf_to_nul >expected &&\n\nSimpler:\n\n    printf \"$expected_value\\0\" >expected &&\n\n> +               git -C repo repo info --format=nul \"$key\" >output &&\n> +               tail -n 1 output >actual &&\n> +               test_cmp expected actual\n> +       '\n\nHow confident are we that `tail -n 1 output >actual` is going to\nperform as expected across platforms and versions of those platforms?\nIt feels awfully fragile to me. Why slice and dice the output anyhow\nrather than merely crafting the correct expected output in the first\nplace and comparing that directly against the actual output? In other\nwords, something like this:\n\n    printf \"$key\\n$expected_value\\0\" >expect &&\n    git -C repo repo info --format=nul \"$key\" >actual &&\n    test_cmp_bin expect actual\n"},{"id":"522870","messageId":"xmqqy0s8url3.fsf@gitster.g","threadId":"63618","inReplyTo":"1642D169-F952-4DD6-8D95-50D5F3C400F1@gmail.com","subject":"Re: [GSoC PATCH v5 1/5] repo: declare the repo command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-28T17:56:40Z","receivedAt":"2025-07-28T17:56:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Hmmm... I'm thinking about what is the \"right thing\" in this context.\n> For example, `git config --list` doesn't quote when in its key=value\n> format. Perhaps I'm citing `git config --list` too much :-), but it is\n> because is the closer command to `git-repo-info` in that sense.\n\nYup.  If we do need to support non-z output format and keep it\nparseable, I am afraid that we cannot avoid quoting.\n\nAn old mistake is not a good excuse to repeat the same one.  Even\nthough the config key names forbid <LF> and <NUL>, \"git config\n--list\" still cannot be used without \"-z\" when the keys may contain\n\"=\", for example, if the caller wants to parse the output correctly\nbecause it was misdesigned to not use any quoting.\n"},{"id":"522906","messageId":"CF7744F0-394B-415D-BECC-9D7CD477B78C@gmail.com","threadId":"63618","inReplyTo":"CAPig+cQn7c5+k06yHOD2jxYTGnny7is=fbo4tOw26eD+4zX-Jw@mail.gmail.com","subject":"Re: [GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2025-07-29T00:15:14Z","receivedAt":"2025-07-29T00:15:26Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"\n> Le 27 juil. 2025 à 18:02, Eric Sunshine <sunshine@sunshineco.com> a écrit :\n> \n> ﻿On Sun, Jul 27, 2025 at 2:02 PM Lucas Seiki Oshiro\n> <lucasseikioshiro@gmail.com> wrote:\n> \n>> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n>> @@ -20,11 +20,20 @@ test_repo_info () {\n>> +       test_expect_success \"null-terminated: $label\" '\n>> +               test_when_finished \"rm -rf repo\" &&\n>> +               eval \"$init_command\" &&\n>> +               echo \"$expected_value\" | lf_to_nul >expected &&\n> \n> Simpler:\n> \n>    printf \"$expected_value\\0\" >expected &&\n\n(Below as well): the shell linter I use does prefer to see\n\n    printf %s\\\\0 \"$var\"\n\nto avoid issues with the variable containing format specifiers. \n\n(Backslash has to be quoted in double-quotes, too, I think? So I left out the quotes here.)\n\n> \n>> +               git -C repo repo info --format=nul \"$key\" >output &&\n>> +               tail -n 1 output >actual &&\n>> +               test_cmp expected actual\n>> +       '\n> \n> How confident are we that `tail -n 1 output >actual` is going to\n> perform as expected across platforms and versions of those platforms?\n> It feels awfully fragile to me. Why slice and dice the output anyhow\n> rather than merely crafting the correct expected output in the first\n> place and comparing that directly against the actual output? In other\n> words, something like this:\n> \n>    printf \"$key\\n$expected_value\\0\" >expect &&\n>    git -C repo repo info --format=nul \"$key\" >actual &&\n>    test_cmp_bin expect actual\n"},{"id":"522908","messageId":"CAPig+cSUnejuxYHvk3hzb_nM6bdEPydBsKgD2FkOX5sfQ2FUAA@mail.gmail.com","threadId":"63618","inReplyTo":"CF7744F0-394B-415D-BECC-9D7CD477B78C@gmail.com","subject":"Re: [GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-07-29T00:27:46Z","receivedAt":"2025-07-29T00:27:58Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jul 28, 2025 at 8:15 PM Ben Knoble <ben.knoble@gmail.com> wrote:\n> > Le 27 juil. 2025 à 18:02, Eric Sunshine <sunshine@sunshineco.com> a écrit :\n> > Simpler:\n> >    printf \"$expected_value\\0\" >expected &&\n>\n> (Below as well): the shell linter I use does prefer to see\n>\n>     printf %s\\\\0 \"$var\"\n>\n> to avoid issues with the variable containing format specifiers.\n\nThat's a very good point about using \"%s\"; I should have suggested\nthat myself. Thanks for the correction.\n\n> (Backslash has to be quoted in double-quotes, too, I think? So I left out the quotes here.)\n\nNo, backslash does not need to be escaped in double-quotes. The\nliteral form `printf \"%s\\0\"` is common enough in Git test scripts, so,\nfor consistency, that should be the form we recommend in reviews, not\nthe form lacking quotes, I'd think.\n\nTaking the above into consideration, the recommendation for Lucas\nwould, therefore, be:\n\n    printf \"%s\\0\" $expected_value >expected &&\n"},{"id":"522909","messageId":"D1CA8142-1090-4492-B11B-F3CAACD0ACC9@gmail.com","threadId":"63618","inReplyTo":"CAPig+cSUnejuxYHvk3hzb_nM6bdEPydBsKgD2FkOX5sfQ2FUAA@mail.gmail.com","subject":"Re: [GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2025-07-29T00:38:39Z","receivedAt":"2025-07-29T00:38:50Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"\n> Le 28 juil. 2025 à 20:27, Eric Sunshine <sunshine@sunshineco.com> a écrit :\n> \n> ﻿On Mon, Jul 28, 2025 at 8:15 PM Ben Knoble <ben.knoble@gmail.com> wrote:\n>>>> Le 27 juil. 2025 à 18:02, Eric Sunshine <sunshine@sunshineco.com> a écrit :\n>>> Simpler:\n>>>   printf \"$expected_value\\0\" >expected &&\n>> \n>> (Below as well): the shell linter I use does prefer to see\n>> \n>>    printf %s\\\\0 \"$var\"\n>> \n>> to avoid issues with the variable containing format specifiers.\n> \n> That's a very good point about using \"%s\"; I should have suggested\n> that myself. Thanks for the correction.\n> \n>> (Backslash has to be quoted in double-quotes, too, I think? So I left out the quotes here.)\n> \n> No, backslash does not need to be escaped in double-quotes. The\n> literal form `printf \"%s\\0\"` is common enough in Git test scripts, so,\n> for consistency, that should be the form we recommend in reviews, not\n> the form lacking quotes, I'd think.\n\nRight; on second thought, shell quotes (mostly) don’t nest like that. Thanks for the catch. "},{"id":"522910","messageId":"CAPig+cQEg+ysR9JmHU57tqbgG+9679yprQ=V89WEup+Ra2Ma-w@mail.gmail.com","threadId":"63618","inReplyTo":"CAPig+cSUnejuxYHvk3hzb_nM6bdEPydBsKgD2FkOX5sfQ2FUAA@mail.gmail.com","subject":"Re: [GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-07-29T00:39:02Z","receivedAt":"2025-07-29T00:39:15Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jul 28, 2025 at 8:27 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> Taking the above into consideration, the recommendation for Lucas\n> would, therefore, be:\n>\n>     printf \"%s\\0\" $expected_value >expected &&\n\nOf course, I accidentally forgot the quotes around the variable\nexpansion. I should have said:\n\n    printf \"%s\\0\" \"$expected_value\" >expected &&\n"},{"id":"522946","messageId":"aIiV4tuKg953QGGE@pks.im","threadId":"63618","inReplyTo":"CAPig+cSBj+kSmzKUarNPQt4qk5p9vjHFGFrJVYmT6TrWHACcEg@mail.gmail.com","subject":"Re: [GSoC PATCH v5 0/5] repo: add new command for retrieving repository info","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-29T09:35:30Z","receivedAt":"2025-07-29T09:35:43Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Sun, Jul 27, 2025 at 04:11:57PM -0400, Eric Sunshine wrote:\n> On Sun, Jul 27, 2025 at 1:51 PM Lucas Seiki Oshiro\n> <lucasseikioshiro@gmail.com> wrote:\n> > This v6 contains small fixes pointed in v5.\n> >\n> > In v5, Patrick and Junio were discussing about quoting the values in the\n> > key=value format using `quote_c_style` [1]. Given that it wouldn't affect\n> > this patchset and it's a simple change that can be done later, I didn't\n> > change it and I'll leave for further discussion when I start to deal\n> > with paths.\n> \n> The counterargument to this stance is that if you employ\n> `quote_c_style` from the onset and document it, then if a future\n> version of Git does start outputting values containing \"funny\"\n> characters for properties which previously did not emit such values\n> (this isn't limited to paths), then consumers who heeded the\n> documentation won't find their tooling breaking suddenly. Tooling may\n> break for consumers who did not heed the documentation, but that will\n> be due to their own shortsightedness, not due to the Git project\n> failing to care about compatibility between versions. For this reason,\n> I'm of the opinion that `quote_c_style` should be used and documented\n> even at this very early stage.\n\nLikewise, I think we should do it now already. In general I don't think\nit should matter much given that tooling should use NUL-terminated modes\nanyway. But it's a rather simple change, so I don't see a strong reason\nto push it into the future.\n\nPatrick\n"},{"id":"522947","messageId":"aIiV7cz2Tx50JZUv@pks.im","threadId":"63618","inReplyTo":"20250727175110.84770-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-29T09:35:41Z","receivedAt":"2025-07-29T09:35:48Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Sun, Jul 27, 2025 at 02:51:07PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index d4f01e35e2..02d5821c77 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -1,12 +1,90 @@\n>  #include \"builtin.h\"\n>  #include \"parse-options.h\"\n> +#include \"refs.h\"\n> +#include \"strbuf.h\"\n>  \n> -static int repo_info(int argc UNUSED, const char **argv UNUSED,\n> -\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n> +typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n\nNice, this now uses a strbuf as recommended.\n\n> +struct field {\n> +\tconst char *key;\n> +\tget_value_fn *get_value;\n> +};\n> +\n> +static int get_references_format(struct repository *repo, struct strbuf *buf)\n> +{\n> +\tstrbuf_addstr(buf,\n> +\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n> +\treturn 0;\n> +}\n\nAnd this prints into the buffer diretcly. Makes sense.\n\n[snip]\n> +static int print_fields(int argc, const char **argv, struct repository *repo)\n> +{\n> +\tconst char *last = \"\";\n> +\n> +\tQSORT(argv, argc, qsort_strcmp);\n> +\n> +\tfor (int i = 0; i < argc; i++) {\n> +\t\tget_value_fn *get_value;\n> +\t\tconst char *key = argv[i];\n> +\t\tstruct strbuf value;\n\nLet's declare the strbuf outside of the loop and `strbuf_reset()` it on\nevery iteration.\n\n> +\n> +\t\tif (!strcmp(key, last))\n> +\t\t\tcontinue;\n> +\n> +\t\tstrbuf_init(&value, 64);\n\nI don't think we should explicitly initialize it with a specific\ncapacity. Let's just use `STRBUF_INIT`.\n\n> +\t\tget_value = get_value_fn_for_key(key);\n> +\n> +\t\tif (!get_value) {\n> +\t\t\tstrbuf_release(&value);\n> +\t\t\treturn error(_(\"key '%s' not found\"), key);\n> +\t\t}\n> +\n> +\t\tget_value(repo, &value);\n> +\t\tprintf(\"%s=%s\\n\", key, value.buf);\n> +\t\tlast = key;\n> +\t\tstrbuf_release(&value);\n\nAnd the call to `strbuf_release()` should be moved to the end of this\nfunction so that we know to reuse the buffer. The above early return\nwould then be converted into a `goto out` so that we have a common exit\npath where we know to clean up all resources.\n\nPatrick\n"},{"id":"523011","messageId":"32163DDA-2DA6-45D0-A877-D305F251FF12@gmail.com","threadId":"63618","inReplyTo":"CAPig+cSBj+kSmzKUarNPQt4qk5p9vjHFGFrJVYmT6TrWHACcEg@mail.gmail.com","subject":"Re: [GSoC PATCH v5 0/5] repo: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-30T15:26:39Z","receivedAt":"2025-07-30T15:26:56Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"First of all: thank you for joining this discussion, Eric!\n\n> For this reason, I'm of the opinion that `quote_c_style` should be\n> used and documented even at this very early stage.\n\nOk, so we have three reviews in favor of using `quote_c_style`. I'll\nuse it in v7.\n\n"},{"id":"523119","messageId":"ACFA8EB5-2392-45AE-9C18-8AA3D0D4BC02@gmail.com","threadId":"63618","inReplyTo":"CAPig+cTuiUy=+2Jf1Lrp1gaM03_zPf8EFMVSKmShqU05t-3aWQ@mail.gmail.com","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-31T19:39:57Z","receivedAt":"2025-07-31T19:40:13Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Based upon the implementation, I can see that the user must type the\n> key in \"dotted\" form:\n> \n>    git repo info references.format\n\nAgreed, that's an important information that was missing in this\ndocumentation.\n\n> I don't think I would figure it out easily. Perhaps hand-holding the\n> user by giving an example would help.\n\nLooks like a good idea. I'll add it in the 4th path of this patchset,\nso we'll have an example with more than one field.\n\n> How can we ensure that the lexicographical-order requirement won't\n> break?\n\nGood point. We don't ensure it through tests. I plan to add an --all\nflag to retrieve all the fields. With that --all flag I can iterate\nand check whether the keys are in the correct order.\n\n> Also, this requirement does feel like a premature optimization. Do\n> you expect this list to become so huge and the corresponding lookup\n> function to be called so frequently that a simple brute-force linear\n> search would be too slow?\n\nIt won't bebig. My plans for this GSoC is to add the object format\nand 9 path-related values, but of course, someone may add more stuff\nto this command in the future.\n\nAbout algorithm complexity, it isn't something that I'm really worried\nabout, but I also don't want to leave some nested loops with strcmps.\nIf I'm not mistaken, this is the complexity of the operations here:\n\n- Sorting the requested keys: O(n*s*log(n))\n- Searching the keys: O(s*log(m))\n- Searching all the requested keys: O(n*s*log(m))\n- The current solution: O(n*s*(log(m) + log(n))\n- The complexity of brute-forcing would be O(n*m*s)\n\nwhere:\n\n- n is the number of the requested fields\n- m is the number of available fields\n- s is the length of the largest requested key\n\nwhich I don't expect to be too big.\n\nOther thing that I should point here is that I also have plans to\nadd a feature for requesting the name of a group of keys and then\nreturn all its internal values. For example:\n\n  $ git repo info layout\n  layout.bare=true\n  layout.shallow=false\n\nHaving everything sorted will make this easier.\n\n> I can see from the implementation that you are sorting the incoming\n> arguments in order to detect and fold out duplicates.\n\nYes, that's the main idea. In the previous versions (where we also\nhad a JSON version), this was done in a more hacky way. Actually,\nsorting the values was a suggestion to make it simpler.\n\n> However, that raises a couple questions. First, is it really a good\n> idea to do something other than what the user asked for?\n\nIn this case, the user isn't asking too much, so we're free here. For\nexample, in git-rev-parse the data is returned in the correct order.\n\n> Second, if this is a good idea, then should the behavior be documented?\n\nOf course, I'll do that!\n\n>    struct strbuf value = STRBUF_INIT;\n>    for (...) {\n>        strbuf_reset(&value);\n>        ...\n>        if (error_condition) {\n>            strbuf_release(...);\n>            return error(...);\n>        }\n>       ...\n>    }\n>    strbuf_release(...);\n\nMuch better, thanks!\n\n> Would the user-experience be\n> improved by instead continuing the loop even after reporting an error,\n> and then adjusting the final `return 0` to conditionally return\n> success or error depending upon whether any keys were unrecognized?\n\nIt seems ok to me, since we're printing some values even if there is an\ninvalid key.\n\n> This is talking about null-terminated format, but the implementation\n> doesn't seem to emit NUL-terminated output at all.\n\nOops. I forgot to change it when rebasing...\n\n> In this case, if you call this function with a distinct repository\n> name each time, then you don't have to remove the repository at all.\n> Moreover, giving each repository a distinct and _meaningful_ name,\n> rather than reusing the same name, could also be helpful when\n> diagnosing failures.\n\nNice solution! I'll do that.\n\n> With only two callers, it's not clear at this point whether the\n> `test_repo_info` function is providing any added value, especially\n> since the additional abstraction increases cognitive load, but perhaps\n> later patches in this series add more callers?\n\nYes. In the next patches of this patchset I'm adding other values (and\nthere are others that will be added in future patchesets). The tests will\nlook very similar, only changing the repository creation, the key and\nthe expected value. Then this will decrease the repetition (and\ncopy-paste typos).\n\nIn the last patch of this series I also add the null-terminated format.\nHaving two formats doubles the number of tests, and this function will\navoid even more code repetition.\n\n"},{"id":"523121","messageId":"3EC7AA57-9863-4F7C-BBB6-D40978CE2748@gmail.com","threadId":"63618","inReplyTo":"aIiV7cz2Tx50JZUv@pks.im","subject":"Re: [GSoC PATCH v5 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-31T19:49:04Z","receivedAt":"2025-07-31T19:49:19Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> And the call to `strbuf_release()` should be moved to the end of this\n> function so that we know to reuse the buffer. The above early return\n> would then be converted into a `goto out` so that we have a common exit\n> path where we know to clean up all resources.\n\nI agree, however, after Eric's suggestion of keep looping even if find\nan invalid key, that goto will now be a continue.\n\nBut I'll use strbuf_reset and move the strbuf_release to the end of the\nfunction in the next version.\n"},{"id":"523153","messageId":"732406E2-DE50-40D5-A8EE-A3C2973D7671@gmail.com","threadId":"63618","inReplyTo":"CAPig+cQn7c5+k06yHOD2jxYTGnny7is=fbo4tOw26eD+4zX-Jw@mail.gmail.com","subject":"Re: [GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-31T23:01:39Z","receivedAt":"2025-07-31T23:01:55Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> I haven't been following the discussion around this series, but don't\n> we also usually provide a `-z` short option? Should that be added for\n> consistency with other commands and to avoid surprising users, or is\n> it too early to commit to that?\n\nYes, it is planned to be added in a future patch!\n\n> something like this:\n> \n>    printf \"$key\\n$expected_value\\0\" >expect &&\n>    git -C repo repo info --format=nul \"$key\" >actual &&\n>    test_cmp_bin expect actual\n\nEven better, and it's consistent to your previous suggestion. I'll\ndo that in the next version.\n"},{"id":"523154","messageId":"D915C846-5DF8-4C1B-AD37-D825A1AA26A6@gmail.com","threadId":"63618","inReplyTo":"20250727175110.84770-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v5 5/5] repo: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-07-31T23:15:31Z","receivedAt":"2025-07-31T23:15:47Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> + const char *const repo_info_usage[] = {\n> + \"git repo info [<key>...]\",\n\nReviewing myself: I missed the `--format` flag here.\n"},{"id":"523197","messageId":"20250801131111.8115-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v7 0/5] repo: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-01T13:11:05Z","receivedAt":"2025-08-01T13:11:22Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\nThese are the changes of this 7th version of `git repo`:\n\n- The tests were refactored (thanks Eric for your careful revision!)\n\n- The documentation has been improved: now it is a little more\n  descriptive about the keys. I'm also including more information about\n  the values\n\n- The documentation now contains examples\n\n- If an invalid key is requested, the command fails, however, now it\n  returns all the valid fields that were requested\n\n- Now, I'm using `quote_c_style` in the key=value format\n\nThanks!\n\nRange-diff versus v6:\n\n1:  bc6f19ba8e ! 1:  b4f063b177 repo: declare the repo command\n    @@ Commit message\n         will bring the functionality of retrieving repository-related\n         information currently returned by `rev-parse`.\n\n    -    Add the required tests, documentation and build changes to enable\n    -    usage of this subcommand.\n    +    Add the required documentation and build changes to enable usage of\n    +    this subcommand.\n\n         Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Helped-by: Junio C Hamano <gitster@pobox.com>\n         Helped-by: Justin Tobler <jltobler@gmail.com>\n    +    Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n         Mentored-by: Patrick Steinhardt <ps@pks.im>\n         Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n    @@ Documentation/git-repo.adoc (new)\n     +\n     +NAME\n     +----\n    -+git-repo - Retrieve information about a repository\n    ++git-repo - Retrieve information about the repository\n     +\n     +SYNOPSIS\n     +--------\n    @@ Documentation/git-repo.adoc (new)\n     +\n     +DESCRIPTION\n     +-----------\n    -+This command retrieve repository level information.\n    ++Retrieve information about the repository.\n     +\n     +THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n     +\n    @@ Documentation/git-repo.adoc (new)\n     +\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n     +\tsection below).\n     +\n    -+INFO KEYS\n    -+---------\n    -+\n    -+The set of data that `git repo` can return is grouped into the following\n    -+categories:\n    -+\n     +SEE ALSO\n     +--------\n     +linkgit:git-rev-parse[1]\n    @@ builtin/repo.c (new)\n     +#include \"builtin.h\"\n     +#include \"parse-options.h\"\n     +\n    ++static const char *const repo_usage[] = {\n    ++\t\"git repo info [<key>...]\",\n    ++\tNULL\n    ++};\n    ++\n     +static int repo_info(int argc UNUSED, const char **argv UNUSED,\n     +\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n     +{\n    @@ builtin/repo.c (new)\n     +\t     struct repository *repo)\n     +{\n     +\tparse_opt_subcommand_fn *fn = NULL;\n    -+\tconst char *const repo_usage[] = {\n    -+\t\t\"git repo info [<key>...]\",\n    -+\t\tNULL\n    -+\t};\n     +\tstruct option options[] = {\n     +\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n     +\t\tOPT_END()\n2:  2b0e91f94d ! 2:  56cb05ecb2 repo: add the field references.format\n    @@ Commit message\n         Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Helped-by: Junio C Hamano <gitster@pobox.com>\n         Helped-by: Justin Tobler <jltobler@gmail.com>\n    +    Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n         Mentored-by: Patrick Steinhardt <ps@pks.im>\n         Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n\n      ## Documentation/git-repo.adoc ##\n    -@@ Documentation/git-repo.adoc: INFO KEYS\n    - The set of data that `git repo` can return is grouped into the following\n    - categories:\n    +@@ Documentation/git-repo.adoc: COMMANDS\n    + \tRetrieve metadata-related information about the current repository. Only\n    + \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n    + \tsection below).\n    +++\n    ++The returned data is lexicographically sorted by the keys.\n    ++\n    ++INFO KEYS\n    ++---------\n    ++\n    ++In order to obtain a set of values from `git repo info`, you should provide\n    ++the keys that identify them. Here's a list of the available keys and the\n    ++values that they return:\n    ++\n    ++`references.format`::\n    ++The reference storage format. The valid values are:\n    +++\n    ++include::ref-storage-format.adoc[]\n\n    -+`references`::\n    -+Reference-related data:\n    -+* `format`: the reference storage format\n    -+\n      SEE ALSO\n      --------\n    - linkgit:git-rev-parse[1]\n\n      ## builtin/repo.c ##\n     @@\n      #include \"builtin.h\"\n      #include \"parse-options.h\"\n    ++#include \"quote.h\"\n     +#include \"refs.h\"\n     +#include \"strbuf.h\"\n\n    + static const char *const repo_usage[] = {\n    + \t\"git repo info [<key>...]\",\n    + \tNULL\n    + };\n    +\n     -static int repo_info(int argc UNUSED, const char **argv UNUSED,\n     -\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n     +typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n    @@ builtin/repo.c\n     +};\n     +\n     +static int get_references_format(struct repository *repo, struct strbuf *buf)\n    -+{\n    + {\n     +\tstrbuf_addstr(buf,\n     +\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n    -+\treturn 0;\n    -+}\n    -+\n    + \treturn 0;\n    + }\n    +\n     +/* repo_info_fields keys should be in lexicographical order */\n     +static const struct field repo_info_fields[] = {\n     +\t{ \"references.format\", get_references_format },\n    @@ builtin/repo.c\n     +}\n     +\n     +static int qsort_strcmp(const void *va, const void *vb)\n    - {\n    ++{\n     +\tconst char *a = *(const char **)va;\n     +\tconst char *b = *(const char **)vb;\n     +\n    @@ builtin/repo.c\n     +\n     +static int print_fields(int argc, const char **argv, struct repository *repo)\n     +{\n    ++\tint ret = 0;\n     +\tconst char *last = \"\";\n    ++\tstruct strbuf sb = STRBUF_INIT;\n     +\n     +\tQSORT(argv, argc, qsort_strcmp);\n     +\n     +\tfor (int i = 0; i < argc; i++) {\n     +\t\tget_value_fn *get_value;\n     +\t\tconst char *key = argv[i];\n    -+\t\tstruct strbuf value;\n    ++\t\tchar *value;\n     +\n     +\t\tif (!strcmp(key, last))\n     +\t\t\tcontinue;\n     +\n    -+\t\tstrbuf_init(&value, 64);\n     +\t\tget_value = get_value_fn_for_key(key);\n     +\n     +\t\tif (!get_value) {\n    -+\t\t\tstrbuf_release(&value);\n    -+\t\t\treturn error(_(\"key '%s' not found\"), key);\n    ++\t\t\tret = error(_(\"key '%s' not found\"), key);\n    ++\t\t\tcontinue;\n     +\t\t}\n     +\n    -+\t\tget_value(repo, &value);\n    -+\t\tprintf(\"%s=%s\\n\", key, value.buf);\n    ++\t\tstrbuf_reset(&sb);\n    ++\t\tget_value(repo, &sb);\n    ++\n    ++\t\tvalue = strbuf_detach(&sb, NULL);\n    ++\t\tquote_c_style(value, &sb, NULL, 0);\n    ++\t\tfree(value);\n    ++\n    ++\t\tprintf(\"%s=%s\\n\", key, sb.buf);\n     +\t\tlast = key;\n    -+\t\tstrbuf_release(&value);\n     +\t}\n     +\n    - \treturn 0;\n    - }\n    -\n    ++\tstrbuf_release(&sb);\n    ++\treturn ret;\n    ++}\n    ++\n     +static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n     +\t\t     struct repository *repo)\n     +{\n    @@ t/t1900-repo.sh (new)\n     +\n     +. ./test-lib.sh\n     +\n    -+# Test if a field is correctly returned in the null-terminated format\n    ++# Test whether a key-value pair is correctly returned\n     +#\n     +# Usage: test_repo_info <label> <init command> <key> <expected value>\n     +#\n     +# Arguments:\n     +#   label: the label of the test\n    -+#   init command: a command that creates a repository called 'repo', configured\n    ++#   init command: a command which creates a repository named with its first argument,\n     +#      accordingly to what is being tested\n     +#   key: the key of the field that is being tested\n     +#   expected value: the value that the field should contain\n     +test_repo_info () {\n     +\tlabel=$1\n     +\tinit_command=$2\n    -+\tkey=$3\n    -+\texpected_value=$4\n    ++\trepo_name=$3\n    ++\tkey=$4\n    ++\texpected_value=$5\n     +\n     +\ttest_expect_success \"$label\" '\n    -+\t\ttest_when_finished \"rm -rf repo\" &&\n    -+\t\teval \"$init_command\" &&\n    -+\t\techo \"$expected_value\" >expected &&\n    -+\t\tgit -C repo repo info \"$key\" >output &&\n    -+\t\tcut -d \"=\" -f 2 <output >actual &&\n    ++\t\teval \"$init_command $repo_name\" &&\n    ++\t\techo \"$key=$expected_value\" >expected &&\n    ++\t\tgit -C $repo_name repo info \"$key\" >actual &&\n     +\t\ttest_cmp expected actual\n     +\t'\n     +}\n     +\n     +test_repo_info 'ref format files is retrieved correctly' '\n    -+\tgit init --ref-format=files repo' 'references.format' 'files'\n    ++\tgit init --ref-format=files' 'format-files' 'references.format' 'files'\n     +\n     +test_repo_info 'ref format reftable is retrieved correctly' '\n    -+\tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n    ++\tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n    ++\n    ++test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    ++\techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n    ++\ttest_must_fail git repo info foo 2>actual_err &&\n    ++\ttest_cmp expected_err actual_err\n    ++'\n     +\n    -+test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n    -+\ttest_when_finished \"rm -rf expected err\" &&\n    -+\techo \"error: key '\\'foo\\'' not found\" >expected &&\n    -+\ttest_must_fail git repo info foo 2>err &&\n    -+\ttest_cmp expected err\n    ++test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n    ++\techo \"references.format=files\" >expected &&\n    ++\ttest_must_fail git repo info foo references.format bar >actual &&\n    ++\ttest_cmp expected actual\n     +'\n     +\n    -+test_expect_success \"only one value is returned if the same key is requested twice\" '\n    -+\ttest_when_finished \"rm -f expected_key expected_value actual_key actual_value output\" &&\n    -+\techo \"references.format\" >expected_key &&\n    -+\tgit rev-parse --show-ref-format >expected_value &&\n    -+\tgit repo info references.format references.format >output &&\n    -+\tcut -d \"=\" -f 1 <output >actual_key &&\n    -+\tcut -d \"=\" -f 2 <output >actual_value &&\n    -+        test_cmp expected_key actual_key &&\n    -+        test_cmp expected_value actual_value\n    ++test_expect_success 'only one value is returned if the same key is requested twice' '\n    ++\tval=$(git rev-parse --show-ref-format) &&\n    ++\techo \"references.format=$val\" >expect &&\n    ++\tgit repo info references.format references.format >actual &&\n    ++\ttest_cmp expect actual\n     +'\n     +\n     +test_done\n3:  733d3533d8 ! 3:  fc4c70d9b7 repo: add field layout.bare\n    @@ Metadata\n     Author: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n\n      ## Commit message ##\n    -    repo: add field layout.bare\n    +    repo: add the field layout.bare\n\n         This commit is part of the series that introduces the new subcommand\n         git-repo-info.\n    @@ Commit message\n         Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Helped-by: Junio C Hamano <gitster@pobox.com>\n         Helped-by: Justin Tobler <jltobler@gmail.com>\n    +    Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n         Mentored-by: Patrick Steinhardt <ps@pks.im>\n         Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n\n      ## Documentation/git-repo.adoc ##\n    -@@ Documentation/git-repo.adoc: categories:\n    - Reference-related data:\n    - * `format`: the reference storage format\n    +@@ Documentation/git-repo.adoc: In order to obtain a set of values from `git repo info`, you should provide\n    + the keys that identify them. Here's a list of the available keys and the\n    + values that they return:\n\n    -+`layout`::\n    -+Information about the how the current repository is represented:\n    -+* `bare`: `true` if this is a bare repository, otherwise `false`.\n    ++`layout.bare`::\n    ++`true` if this is a bare repository, otherwise `false`.\n     +\n    - SEE ALSO\n    - --------\n    - linkgit:git-rev-parse[1]\n    + `references.format`::\n    + The reference storage format. The valid values are:\n    + +\n\n      ## builtin/repo.c ##\n     @@\n    @@ builtin/repo.c\n      #include \"builtin.h\"\n     +#include \"environment.h\"\n      #include \"parse-options.h\"\n    + #include \"quote.h\"\n      #include \"refs.h\"\n    - #include \"strbuf.h\"\n     @@ builtin/repo.c: struct field {\n      \tget_value_fn *get_value;\n      };\n    @@ builtin/repo.c: static int get_references_format(struct repository *repo, struct\n      ## t/t1900-repo.sh ##\n     @@ t/t1900-repo.sh: test_repo_info 'ref format files is retrieved correctly' '\n      test_repo_info 'ref format reftable is retrieved correctly' '\n    - \tgit init --ref-format=reftable repo' 'references.format' 'reftable'\n    + \tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n\n     +test_repo_info 'bare repository = false is retrieved correctly' '\n    -+\tgit init repo' 'layout.bare' 'false'\n    ++\tgit init' 'bare' 'layout.bare' 'false'\n     +\n     +test_repo_info 'bare repository = true is retrieved correctly' '\n    -+\tgit init --bare repo' 'layout.bare' 'true'\n    ++\tgit init --bare' 'nonbare' 'layout.bare' 'true'\n     +\n    - test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n    - \ttest_when_finished \"rm -rf expected err\" &&\n    - \techo \"error: key '\\'foo\\'' not found\" >expected &&\n    + test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    + \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n    + \ttest_must_fail git repo info foo 2>actual_err &&\n    +@@ t/t1900-repo.sh: test_expect_success 'only one value is returned if the same key is requested twi\n    + \ttest_cmp expect actual\n    + '\n    +\n    ++test_expect_success 'output is returned correctly when two keys are requested' '\n    ++\tcat >expect <<-\\EOF &&\n    ++\tlayout.bare=false\n    ++\treferences.format=files\n    ++\tEOF\n    ++\tgit init --ref-format=files two-keys &&\n    ++\tgit -C two-keys repo info layout.bare references.format\n    ++'\n    + test_done\n4:  fa17719ebc ! 4:  f35704442a repo: add field layout.shallow\n    @@ Metadata\n     Author: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n\n      ## Commit message ##\n    -    repo: add field layout.shallow\n    +    repo: add the field layout.shallow\n\n         This commit is part of the series that introduces the new subcommand\n         git-repo-info.\n    @@ Commit message\n         Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Helped-by: Junio C Hamano <gitster@pobox.com>\n         Helped-by: Justin Tobler <jltobler@gmail.com>\n    +    Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n         Mentored-by: Patrick Steinhardt <ps@pks.im>\n         Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n\n      ## Documentation/git-repo.adoc ##\n    -@@ Documentation/git-repo.adoc: Reference-related data:\n    - `layout`::\n    - Information about the how the current repository is represented:\n    - * `bare`: `true` if this is a bare repository, otherwise `false`.\n    -+* `shallow`: `true` if this is a shallow repository, otherwise `false`.\n    +@@ Documentation/git-repo.adoc: values that they return:\n    + `layout.bare`::\n    + `true` if this is a bare repository, otherwise `false`.\n\n    - SEE ALSO\n    - --------\n    ++`layout.shallow`::\n    ++`true` if this is a shallow repository, otherwise `false`.\n    ++\n    + `references.format`::\n    + The reference storage format. The valid values are:\n    + +\n\n      ## builtin/repo.c ##\n     @@\n    - #include \"parse-options.h\"\n    + #include \"quote.h\"\n      #include \"refs.h\"\n      #include \"strbuf.h\"\n     +#include \"shallow.h\"\n\n    - typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n    -\n    + static const char *const repo_usage[] = {\n    + \t\"git repo info [<key>...]\",\n     @@ builtin/repo.c: static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n      \treturn 0;\n      }\n    @@ builtin/repo.c: static int get_references_format(struct repository *repo, struct\n      ## t/t1900-repo.sh ##\n     @@ t/t1900-repo.sh: test_repo_info 'bare repository = false is retrieved correctly' '\n      test_repo_info 'bare repository = true is retrieved correctly' '\n    - \tgit init --bare repo' 'layout.bare' 'true'\n    + \tgit init --bare' 'nonbare' 'layout.bare' 'true'\n\n     +test_repo_info 'shallow repository = false is retrieved correctly' '\n    -+\tgit init repo' 'layout.shallow' 'false'\n    ++\tgit init' 'nonshallow' 'layout.shallow' 'false'\n     +\n     +test_repo_info 'shallow repository = true is retrieved correctly' '\n     +\tgit init remote &&\n    -+\tcd remote &&\n    -+\techo x >x &&\n    -+\tgit add x &&\n    -+\tgit commit -m x &&\n    -+\tcd .. &&\n    -+\tgit clone --depth 1 \"file://$PWD/remote\" repo &&\n    -+\trm -rf remote\n    -+\t' 'layout.shallow' 'true'\n    ++\techo x >remote/x &&\n    ++\tgit -C remote add x &&\n    ++\tgit -C remote commit -m x &&\n    ++\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n     +\n    - test_expect_success 'git-repo-info aborts if an invalid key is requested' '\n    - \ttest_when_finished \"rm -rf expected err\" &&\n    - \techo \"error: key '\\'foo\\'' not found\" >expected &&\n    -@@ t/t1900-repo.sh: test_expect_success \"only one value is returned if the same key is requested twi\n    -         test_cmp expected_value actual_value\n    + test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    + \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n    + \ttest_must_fail git repo info foo 2>actual_err &&\n    +@@ t/t1900-repo.sh: test_expect_success 'output is returned correctly when two keys are requested' '\n    + \tgit init --ref-format=files two-keys &&\n    + \tgit -C two-keys repo info layout.bare references.format\n      '\n    -\n    -+test_expect_success 'output is returned correctly when two keys are requested' '\n    -+\ttest_when_finished \"rm -f expect\" &&\n    -+\tprintf \"layout.bare=false\\nlayout.shallow=false\\n\" >expect &&\n    -+\tgit repo info layout.shallow layout.bare >actual &&\n    -+\ttest_cmp expect actual\n    -+'\n     +\n      test_done\n5:  b72a61b73b ! 5:  8931b12eca repo: add the --format flag\n    @@ Commit message\n         Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Helped-by: Junio C Hamano <gitster@pobox.com>\n         Helped-by: Justin Tobler <jltobler@gmail.com>\n    +    Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n         Mentored-by: Patrick Steinhardt <ps@pks.im>\n         Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n    @@ Documentation/git-repo.adoc: THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHAN\n      COMMANDS\n      --------\n     -`info [<key>...]`::\n    -+`info [--format=<format>] [<key>...]`::\n    ++`info [--format=<keyvalue|nul>] [<key>...]`::\n      \tRetrieve metadata-related information about the current repository. Only\n      \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n      \tsection below).\n    + +\n    + The returned data is lexicographically sorted by the keys.\n     ++\n     +The output format can be chosen through the flag `--format`. Two formats are\n     +supported:\n    @@ Documentation/git-repo.adoc: THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHAN\n\n      INFO KEYS\n      ---------\n    +@@ Documentation/git-repo.adoc: The reference storage format. The valid values are:\n    + +\n    + include::ref-storage-format.adoc[]\n    +\n    ++\n    ++Examples\n    ++--------\n    ++\n    ++* Retrieves the reference format of the current repository:\n    +++\n    ++------------\n    ++git repo info references.format\n    ++------------\n    +++\n    ++\n    ++* Retrieves whether the current repository is bare and whether it is shallow\n    ++using the `nul` format:\n    +++\n    ++------------\n    ++git repo info --format=nul layout.bare layout.shallow\n    ++------------\n    ++\n    + SEE ALSO\n    + --------\n    + linkgit:git-rev-parse[1]\n\n      ## builtin/repo.c ##\n     @@\n    + #include \"shallow.h\"\n    +\n    + static const char *const repo_usage[] = {\n    +-\t\"git repo info [<key>...]\",\n    ++\t\"git repo info [--format=<keyvalue|nul>] [<key>...]\",\n    + \tNULL\n    + };\n\n      typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n\n    @@ builtin/repo.c: static int qsort_strcmp(const void *va, const void *vb)\n     +\t\t\tstruct repository *repo,\n     +\t\t\tenum output_format format)\n      {\n    + \tint ret = 0;\n      \tconst char *last = \"\";\n    + \tstruct strbuf sb = STRBUF_INIT;\n    +\n     +\tchar kv_sep;\n     +\tchar field_sep;\n     +\n    @@ builtin/repo.c: static int qsort_strcmp(const void *va, const void *vb)\n     +\t\tfield_sep = '\\0';\n     +\t\tbreak;\n     +\t}\n    -\n    ++\n      \tQSORT(argv, argc, qsort_strcmp);\n\n    + \tfor (int i = 0; i < argc; i++) {\n    + \t\tget_value_fn *get_value;\n    + \t\tconst char *key = argv[i];\n    +-\t\tchar *value;\n    +\n    + \t\tif (!strcmp(key, last))\n    + \t\t\tcontinue;\n     @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repository *repo)\n    - \t\t}\n    + \t\tstrbuf_reset(&sb);\n    + \t\tget_value(repo, &sb);\n    +\n    +-\t\tvalue = strbuf_detach(&sb, NULL);\n    +-\t\tquote_c_style(value, &sb, NULL, 0);\n    +-\t\tfree(value);\n    ++\t\tif (format == FORMAT_KEYVALUE) {\n    ++\t\t\tchar *value;\n    ++\t\t\tvalue = strbuf_detach(&sb, NULL);\n    ++\t\t\tquote_c_style(value, &sb, NULL, 0);\n    ++\t\t\tfree(value);\n    ++\t\t}\n\n    - \t\tget_value(repo, &value);\n    --\t\tprintf(\"%s=%s\\n\", key, value.buf);\n    -+\t\tprintf(\"%s%c%s%c\", key, kv_sep, value.buf, field_sep);\n    +-\t\tprintf(\"%s=%s\\n\", key, sb.buf);\n    ++\t\tprintf(\"%s%c%s%c\", key, kv_sep, sb.buf, field_sep);\n      \t\tlast = key;\n    - \t\tstrbuf_release(&value);\n      \t}\n    +\n     @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repository *repo)\n    - \treturn 0;\n    + \treturn ret;\n      }\n\n     -static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n    @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repo\n     -\treturn print_fields(argc - 1, argv + 1, repo);\n     +\tconst char *format_str = \"keyvalue\";\n     +\tenum output_format format;\n    -+\tconst char *const repo_info_usage[] = {\n    -+\t\t\"git repo info [<key>...]\",\n    -+\t\tNULL\n    -+\t};\n     +\tstruct option options[] = {\n     +\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n     +\t\t\t   N_(\"output format\")),\n     +\t\tOPT_END()\n     +\t};\n     +\n    -+\targc = parse_options(argc, argv, prefix, options, repo_info_usage, 0);\n    ++\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n     +\n     +\tif (!strcmp(format_str, \"keyvalue\"))\n     +\t\tformat = FORMAT_KEYVALUE;\n    @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repo\n\n      ## t/t1900-repo.sh ##\n     @@ t/t1900-repo.sh: test_repo_info () {\n    - \tkey=$3\n    - \texpected_value=$4\n    + \tkey=$4\n    + \texpected_value=$5\n\n     -\ttest_expect_success \"$label\" '\n    -+\ttest_expect_success \"null-terminated: $label\" '\n    -+\t\ttest_when_finished \"rm -rf repo\" &&\n    -+\t\teval \"$init_command\" &&\n    -+\t\techo \"$expected_value\" | lf_to_nul >expected &&\n    -+\t\tgit -C repo repo info --format=nul \"$key\" >output &&\n    -+\t\ttail -n 1 output >actual &&\n    +-\t\teval \"$init_command $repo_name\" &&\n    ++\ttest_expect_success \"keyvalue: $label\" '\n    ++\t\teval \"$init_command keyvalue-$repo_name\" &&\n    + \t\techo \"$key=$expected_value\" >expected &&\n    +-\t\tgit -C $repo_name repo info \"$key\" >actual &&\n    ++\t\tgit -C keyvalue-$repo_name repo info \"$key\" >actual &&\n     +\t\ttest_cmp expected actual\n     +\t'\n     +\n    -+\ttest_expect_success \"key-value: $label\" '\n    - \t\ttest_when_finished \"rm -rf repo\" &&\n    - \t\teval \"$init_command\" &&\n    - \t\techo \"$expected_value\" >expected &&\n    --\t\tgit -C repo repo info \"$key\" >output &&\n    -+\t\tgit -C repo repo info --format=keyvalue \"$key\" >output &&\n    - \t\tcut -d \"=\" -f 2 <output >actual &&\n    ++\ttest_expect_success \"nul: $label\" '\n    ++\t\teval \"$init_command nul-$repo_name\" &&\n    ++\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n    ++\t\tgit -C nul-$repo_name repo info --format=nul \"$key\" >actual &&\n      \t\ttest_cmp expected actual\n      \t'\n    + }\n    +@@ t/t1900-repo.sh: test_repo_info 'shallow repository = false is retrieved correctly' '\n    + \tgit init' 'nonshallow' 'layout.shallow' 'false'\n    +\n    + test_repo_info 'shallow repository = true is retrieved correctly' '\n    ++\ttest_when_finished \"rm -rf remote\" &&\n    + \tgit init remote &&\n    + \techo x >remote/x &&\n    + \tgit -C remote add x &&\n     @@ t/t1900-repo.sh: test_expect_success 'output is returned correctly when two keys are requested' '\n    - \ttest_cmp expect actual\n    + \tgit -C two-keys repo info layout.bare references.format\n      '\n\n     +test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n\nLucas Seiki Oshiro (5):\n  repo: declare the repo command\n  repo: add the field references.format\n  repo: add the field layout.bare\n  repo: add the field layout.shallow\n  repo: add the --format flag\n\n .gitignore                  |   1 +\n Documentation/git-repo.adoc |  81 +++++++++++++++++\n Documentation/meson.build   |   1 +\n Makefile                    |   1 +\n builtin.h                   |   1 +\n builtin/repo.c              | 173 ++++++++++++++++++++++++++++++++++++\n command-list.txt            |   1 +\n git.c                       |   1 +\n meson.build                 |   1 +\n t/meson.build               |   1 +\n t/t1900-repo.sh             |  97 ++++++++++++++++++++\n 11 files changed, 359 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n create mode 100755 t/t1900-repo.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523198","messageId":"20250801131111.8115-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250801131111.8115-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v7 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-01T13:11:06Z","receivedAt":"2025-08-01T13:11:26Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, `git rev-parse` covers a wide range of functionality not\ndirectly related to parsing revisions, as its name suggests. Over time,\nmany features like parsing datestrings, options, paths, and others\nwere added to it because there wasn't a more appropriate command\nto place them.\n\nCreate a new Git command called `repo`. `git repo` will be the main\ncommand for obtaining the information about a repository (such as\nmetadata and metrics).\n\nAlso declare a subcommand for `repo` called `info`. `git repo info`\nwill bring the functionality of retrieving repository-related\ninformation currently returned by `rev-parse`.\n\nAdd the required documentation and build changes to enable usage of\nthis subcommand.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore                  |  1 +\n Documentation/git-repo.adoc | 32 ++++++++++++++++++++++++++++++++\n Documentation/meson.build   |  1 +\n Makefile                    |  1 +\n builtin.h                   |  1 +\n builtin/repo.c              | 27 +++++++++++++++++++++++++++\n command-list.txt            |  1 +\n git.c                       |  1 +\n meson.build                 |  1 +\n 9 files changed, 66 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..1803023427 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nnew file mode 100644\nindex 0000000000..68c706f5a0\n--- /dev/null\n+++ b/Documentation/git-repo.adoc\n@@ -0,0 +1,32 @@\n+git-repo(1)\n+===========\n+\n+NAME\n+----\n+git-repo - Retrieve information about the repository\n+\n+SYNOPSIS\n+--------\n+[synopsis]\n+git repo info [<key>...]\n+\n+DESCRIPTION\n+-----------\n+Retrieve information about the repository.\n+\n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n+COMMANDS\n+--------\n+`info [<key>...]`::\n+\tRetrieve metadata-related information about the current repository. Only\n+\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n+\tsection below).\n+\n+SEE ALSO\n+--------\n+linkgit:git-rev-parse[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 4404c623f0..41f43e0336 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -116,6 +116,7 @@ manpages = {\n   'git-repack.adoc' : 1,\n   'git-replace.adoc' : 1,\n   'git-replay.adoc' : 1,\n+  'git-repo.adoc' : 1,\n   'git-request-pull.adoc' : 1,\n   'git-rerere.adoc' : 1,\n   'git-reset.adoc' : 1,\ndiff --git a/Makefile b/Makefile\nindex 5f7dd79dfa..9dce446309 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1306,6 +1306,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..e6458e6fb9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo.c b/builtin/repo.c\nnew file mode 100644\nindex 0000000000..fd2a9b4216\n--- /dev/null\n+++ b/builtin/repo.c\n@@ -0,0 +1,27 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+static const char *const repo_usage[] = {\n+\t\"git repo info [<key>...]\",\n+\tNULL\n+};\n+\n+static int repo_info(int argc UNUSED, const char **argv UNUSED,\n+\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+{\n+\treturn 0;\n+}\n+\n+int cmd_repo(int argc, const char **argv, const char *prefix,\n+\t     struct repository *repo)\n+{\n+\tparse_opt_subcommand_fn *fn = NULL;\n+\tstruct option options[] = {\n+\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\treturn fn(argc, argv, prefix, repo);\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex b7ade3ab9f..1b0bdee00d 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -164,6 +164,7 @@ git-remote                              ancillarymanipulators           complete\n git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-replay                              plumbingmanipulators\n+git-repo                                plumbinginterrogators\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           history\ndiff --git a/git.c b/git.c\nindex 07a5fe39fb..8290d8b8c8 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo\", cmd_repo, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 9bc1826cb6..8819b64f93 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523199","messageId":"20250801131111.8115-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250801131111.8115-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v7 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-01T13:11:07Z","receivedAt":"2025-08-01T13:11:30Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--show-ref-format` from git-rev-parse is used for retrieving\nthe reference format (i.e. `files` or `reftable`). This way, it is\nused for querying repository metadata, fitting in the purpose of\ngit-repo-info.\n\nAdd a new field `references.format` to the repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 14 ++++++\n builtin/repo.c              | 90 ++++++++++++++++++++++++++++++++++++-\n t/meson.build               |  1 +\n t/t1900-repo.sh             | 57 +++++++++++++++++++++++\n 4 files changed, 160 insertions(+), 2 deletions(-)\n create mode 100755 t/t1900-repo.sh\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 68c706f5a0..2dd130f3dd 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -22,6 +22,20 @@ COMMANDS\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n++\n+The returned data is lexicographically sorted by the keys.\n+\n+INFO KEYS\n+---------\n+\n+In order to obtain a set of values from `git repo info`, you should provide\n+the keys that identify them. Here's a list of the available keys and the\n+values that they return:\n+\n+`references.format`::\n+The reference storage format. The valid values are:\n++\n+include::ref-storage-format.adoc[]\n \n SEE ALSO\n --------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex fd2a9b4216..6824e50d00 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,17 +1,103 @@\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"quote.h\"\n+#include \"refs.h\"\n+#include \"strbuf.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n \tNULL\n };\n \n-static int repo_info(int argc UNUSED, const char **argv UNUSED,\n-\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n+\n+struct field {\n+\tconst char *key;\n+\tget_value_fn *get_value;\n+};\n+\n+static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n+\tstrbuf_addstr(buf,\n+\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n \treturn 0;\n }\n \n+/* repo_info_fields keys should be in lexicographical order */\n+static const struct field repo_info_fields[] = {\n+\t{ \"references.format\", get_references_format },\n+};\n+\n+static int repo_info_fields_cmp(const void *va, const void *vb)\n+{\n+\tconst struct field *a = va;\n+\tconst struct field *b = vb;\n+\n+\treturn strcmp(a->key, b->key);\n+}\n+\n+static get_value_fn *get_value_fn_for_key(const char *key)\n+{\n+\tconst struct field search_key = { key, NULL };\n+\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n+\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n+\t\t\t\t\t    sizeof(*found),\n+\t\t\t\t\t    repo_info_fields_cmp);\n+\treturn found ? found->get_value : NULL;\n+}\n+\n+static int qsort_strcmp(const void *va, const void *vb)\n+{\n+\tconst char *a = *(const char **)va;\n+\tconst char *b = *(const char **)vb;\n+\n+\treturn strcmp(a, b);\n+}\n+\n+static int print_fields(int argc, const char **argv, struct repository *repo)\n+{\n+\tint ret = 0;\n+\tconst char *last = \"\";\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tQSORT(argv, argc, qsort_strcmp);\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tget_value_fn *get_value;\n+\t\tconst char *key = argv[i];\n+\t\tchar *value;\n+\n+\t\tif (!strcmp(key, last))\n+\t\t\tcontinue;\n+\n+\t\tget_value = get_value_fn_for_key(key);\n+\n+\t\tif (!get_value) {\n+\t\t\tret = error(_(\"key '%s' not found\"), key);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tstrbuf_reset(&sb);\n+\t\tget_value(repo, &sb);\n+\n+\t\tvalue = strbuf_detach(&sb, NULL);\n+\t\tquote_c_style(value, &sb, NULL, 0);\n+\t\tfree(value);\n+\n+\t\tprintf(\"%s=%s\\n\", key, sb.buf);\n+\t\tlast = key;\n+\t}\n+\n+\tstrbuf_release(&sb);\n+\treturn ret;\n+}\n+\n+static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+\t\t     struct repository *repo)\n+{\n+\treturn print_fields(argc - 1, argv + 1, repo);\n+}\n+\n int cmd_repo(int argc, const char **argv, const char *prefix,\n \t     struct repository *repo)\n {\ndiff --git a/t/meson.build b/t/meson.build\nindex 660d780dcc..5de9c3c7e9 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -245,6 +245,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nnew file mode 100755\nindex 0000000000..9796e36087\n--- /dev/null\n+++ b/t/t1900-repo.sh\n@@ -0,0 +1,57 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+\n+. ./test-lib.sh\n+\n+# Test whether a key-value pair is correctly returned\n+#\n+# Usage: test_repo_info <label> <init command> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init command: a command which creates a repository named with its first argument,\n+#      accordingly to what is being tested\n+#   key: the key of the field that is being tested\n+#   expected value: the value that the field should contain\n+test_repo_info () {\n+\tlabel=$1\n+\tinit_command=$2\n+\trepo_name=$3\n+\tkey=$4\n+\texpected_value=$5\n+\n+\ttest_expect_success \"$label\" '\n+\t\teval \"$init_command $repo_name\" &&\n+\t\techo \"$key=$expected_value\" >expected &&\n+\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n+\n+test_repo_info 'ref format files is retrieved correctly' '\n+\tgit init --ref-format=files' 'format-files' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' '\n+\tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n+\n+test_expect_success 'git-repo-info fails if an invalid key is requested' '\n+\techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n+\ttest_must_fail git repo info foo 2>actual_err &&\n+\ttest_cmp expected_err actual_err\n+'\n+\n+test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n+\techo \"references.format=files\" >expected &&\n+\ttest_must_fail git repo info foo references.format bar >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'only one value is returned if the same key is requested twice' '\n+\tval=$(git rev-parse --show-ref-format) &&\n+\techo \"references.format=$val\" >expect &&\n+\tgit repo info references.format references.format >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523200","messageId":"20250801131111.8115-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250801131111.8115-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v7 3/5] repo: add the field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-01T13:11:08Z","receivedAt":"2025-08-01T13:11:33Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag --is-bare-repository from git-rev-parse is used for retrieving\nwhether the current repository is bare. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field layout.bare to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              | 11 +++++++++++\n t/t1900-repo.sh             | 14 ++++++++++++++\n 3 files changed, 28 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 2dd130f3dd..98358c5539 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -32,6 +32,9 @@ In order to obtain a set of values from `git repo info`, you should provide\n the keys that identify them. Here's a list of the available keys and the\n values that they return:\n \n+`layout.bare`::\n+`true` if this is a bare repository, otherwise `false`.\n+\n `references.format`::\n The reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 6824e50d00..2d51bfa195 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,4 +1,7 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n+#include \"environment.h\"\n #include \"parse-options.h\"\n #include \"quote.h\"\n #include \"refs.h\"\n@@ -16,6 +19,13 @@ struct field {\n \tget_value_fn *get_value;\n };\n \n+static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf,\n+\t\t      is_bare_repository() ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -25,6 +35,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n \n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n+\t{ \"layout.bare\", get_layout_bare },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 9796e36087..6c555e90c3 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -35,6 +35,12 @@ test_repo_info 'ref format files is retrieved correctly' '\n test_repo_info 'ref format reftable is retrieved correctly' '\n \tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' '\n+\tgit init' 'bare' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' '\n+\tgit init --bare' 'nonbare' 'layout.bare' 'true'\n+\n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n \ttest_must_fail git repo info foo 2>actual_err &&\n@@ -54,4 +60,12 @@ test_expect_success 'only one value is returned if the same key is requested twi\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'output is returned correctly when two keys are requested' '\n+\tcat >expect <<-\\EOF &&\n+\tlayout.bare=false\n+\treferences.format=files\n+\tEOF\n+\tgit init --ref-format=files two-keys &&\n+\tgit -C two-keys repo info layout.bare references.format\n+'\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523201","messageId":"20250801131111.8115-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250801131111.8115-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v7 4/5] repo: add the field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-01T13:11:09Z","receivedAt":"2025-08-01T13:11:37Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--is-shallow-repository` from git-rev-parse is used for\nretrieving whether the repository is shallow. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field `layout.shallow` to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              |  9 +++++++++\n t/t1900-repo.sh             | 11 +++++++++++\n 3 files changed, 23 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 98358c5539..140ee3a0aa 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -35,6 +35,9 @@ values that they return:\n `layout.bare`::\n `true` if this is a bare repository, otherwise `false`.\n \n+`layout.shallow`::\n+`true` if this is a shallow repository, otherwise `false`.\n+\n `references.format`::\n The reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 2d51bfa195..56c3a4027f 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -6,6 +6,7 @@\n #include \"quote.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n+#include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n@@ -26,6 +27,13 @@ static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_layout_shallow(struct repository *repo, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf,\n+\t\t      is_repository_shallow(repo) ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -36,6 +44,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n \t{ \"layout.bare\", get_layout_bare },\n+\t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 6c555e90c3..6706cb4c44 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -41,6 +41,16 @@ test_repo_info 'bare repository = false is retrieved correctly' '\n test_repo_info 'bare repository = true is retrieved correctly' '\n \tgit init --bare' 'nonbare' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' '\n+\tgit init' 'nonshallow' 'layout.shallow' 'false'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' '\n+\tgit init remote &&\n+\techo x >remote/x &&\n+\tgit -C remote add x &&\n+\tgit -C remote commit -m x &&\n+\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n+\n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n \ttest_must_fail git repo info foo 2>actual_err &&\n@@ -68,4 +78,5 @@ test_expect_success 'output is returned correctly when two keys are requested' '\n \tgit init --ref-format=files two-keys &&\n \tgit -C two-keys repo info layout.bare references.format\n '\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523202","messageId":"20250801131111.8115-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250801131111.8115-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v7 5/5] repo: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-01T13:11:10Z","receivedAt":"2025-08-01T13:11:40Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the --format flag to git-repo-info. By using this flag, the users\ncan choose the format for obtaining the data they requested.\n\nGiven that this command can be used for generating input for other\napplications and for being read by end users, it requires at least two\nformats: one for being read by humans and other for being read by\nmachines. Some other Git commands also have two output formats, notably\ngit-config which was the inspiration for the two formats that were\nchosen here:\n\n- keyvalue, where the retrieved data is printed one per line, using =\n  for delimiting the key and the value. This is the default format,\n  targeted for end users.\n- nul, where the retrieved data is separated by null characters, using\n  the newline character for delimiting the key and the value. This\n  format is targeted for being read by machines.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 31 +++++++++++++++++++-\n builtin/repo.c              | 58 +++++++++++++++++++++++++++++++------\n t/t1900-repo.sh             | 21 ++++++++++++--\n 3 files changed, 97 insertions(+), 13 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 140ee3a0aa..b735cf4737 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -18,12 +18,23 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n \n COMMANDS\n --------\n-`info [<key>...]`::\n+`info [--format=<keyvalue|nul>] [<key>...]`::\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n +\n The returned data is lexicographically sorted by the keys.\n++\n+The output format can be chosen through the flag `--format`. Two formats are\n+supported:\n++\n+* `keyvalue`: output key-value pairs one per line using the `=` character as\n+the delimiter between the key and the value. This is the default.\n+\n+* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n+between the key and the value and using a null character after each value.\n+This format is better suited for being parsed by another applications than\n+`keyvalue`.\n \n INFO KEYS\n ---------\n@@ -43,6 +54,24 @@ The reference storage format. The valid values are:\n +\n include::ref-storage-format.adoc[]\n \n+\n+Examples\n+--------\n+\n+* Retrieves the reference format of the current repository:\n++\n+------------\n+git repo info references.format\n+------------\n++\n+\n+* Retrieves whether the current repository is bare and whether it is shallow\n+using the `nul` format:\n++\n+------------\n+git repo info --format=nul layout.bare layout.shallow\n+------------\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 56c3a4027f..4015cf88b7 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -9,12 +9,17 @@\n #include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n-\t\"git repo info [<key>...]\",\n+\t\"git repo info [--format=<keyvalue|nul>] [<key>...]\",\n \tNULL\n };\n \n typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n \n+enum output_format {\n+\tFORMAT_KEYVALUE,\n+\tFORMAT_NUL_TERMINATED,\n+};\n+\n struct field {\n \tconst char *key;\n \tget_value_fn *get_value;\n@@ -74,18 +79,33 @@ static int qsort_strcmp(const void *va, const void *vb)\n \treturn strcmp(a, b);\n }\n \n-static int print_fields(int argc, const char **argv, struct repository *repo)\n+static int print_fields(int argc, const char **argv,\n+\t\t\tstruct repository *repo,\n+\t\t\tenum output_format format)\n {\n \tint ret = 0;\n \tconst char *last = \"\";\n \tstruct strbuf sb = STRBUF_INIT;\n \n+\tchar kv_sep;\n+\tchar field_sep;\n+\n+\tswitch (format) {\n+\tcase FORMAT_KEYVALUE:\n+\t\tkv_sep = '=';\n+\t\tfield_sep = '\\n';\n+\t\tbreak;\n+\tcase FORMAT_NUL_TERMINATED:\n+\t\tkv_sep = '\\n';\n+\t\tfield_sep = '\\0';\n+\t\tbreak;\n+\t}\n+\n \tQSORT(argv, argc, qsort_strcmp);\n \n \tfor (int i = 0; i < argc; i++) {\n \t\tget_value_fn *get_value;\n \t\tconst char *key = argv[i];\n-\t\tchar *value;\n \n \t\tif (!strcmp(key, last))\n \t\t\tcontinue;\n@@ -100,11 +120,14 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \t\tstrbuf_reset(&sb);\n \t\tget_value(repo, &sb);\n \n-\t\tvalue = strbuf_detach(&sb, NULL);\n-\t\tquote_c_style(value, &sb, NULL, 0);\n-\t\tfree(value);\n+\t\tif (format == FORMAT_KEYVALUE) {\n+\t\t\tchar *value;\n+\t\t\tvalue = strbuf_detach(&sb, NULL);\n+\t\t\tquote_c_style(value, &sb, NULL, 0);\n+\t\t\tfree(value);\n+\t\t}\n \n-\t\tprintf(\"%s=%s\\n\", key, sb.buf);\n+\t\tprintf(\"%s%c%s%c\", key, kv_sep, sb.buf, field_sep);\n \t\tlast = key;\n \t}\n \n@@ -112,10 +135,27 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \treturn ret;\n }\n \n-static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+static int repo_info(int argc, const char **argv, const char *prefix,\n \t\t     struct repository *repo)\n {\n-\treturn print_fields(argc - 1, argv + 1, repo);\n+\tconst char *format_str = \"keyvalue\";\n+\tenum output_format format;\n+\tstruct option options[] = {\n+\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n+\t\t\t   N_(\"output format\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\tif (!strcmp(format_str, \"keyvalue\"))\n+\t\tformat = FORMAT_KEYVALUE;\n+\telse if (!strcmp(format_str, \"nul\"))\n+\t\tformat = FORMAT_NUL_TERMINATED;\n+\telse\n+\t\tdie(_(\"invalid format '%s'\"), format_str);\n+\n+\treturn print_fields(argc, argv, repo, format);\n }\n \n int cmd_repo(int argc, const char **argv, const char *prefix,\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 6706cb4c44..463ec3af92 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -21,10 +21,17 @@ test_repo_info () {\n \tkey=$4\n \texpected_value=$5\n \n-\ttest_expect_success \"$label\" '\n-\t\teval \"$init_command $repo_name\" &&\n+\ttest_expect_success \"keyvalue: $label\" '\n+\t\teval \"$init_command keyvalue-$repo_name\" &&\n \t\techo \"$key=$expected_value\" >expected &&\n-\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\t\tgit -C keyvalue-$repo_name repo info \"$key\" >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+\n+\ttest_expect_success \"nul: $label\" '\n+\t\teval \"$init_command nul-$repo_name\" &&\n+\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n+\t\tgit -C nul-$repo_name repo info --format=nul \"$key\" >actual &&\n \t\ttest_cmp expected actual\n \t'\n }\n@@ -45,6 +52,7 @@ test_repo_info 'shallow repository = false is retrieved correctly' '\n \tgit init' 'nonshallow' 'layout.shallow' 'false'\n \n test_repo_info 'shallow repository = true is retrieved correctly' '\n+\ttest_when_finished \"rm -rf remote\" &&\n \tgit init remote &&\n \techo x >remote/x &&\n \tgit -C remote add x &&\n@@ -79,4 +87,11 @@ test_expect_success 'output is returned correctly when two keys are requested' '\n \tgit -C two-keys repo info layout.bare references.format\n '\n \n+test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n+\ttest_when_finished \"rm -f err expected\" &&\n+\techo \"fatal: invalid format '\\'foo\\''\" >expected &&\n+\ttest_must_fail git repo info --format=foo 2>err &&\n+\ttest_cmp expected err\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523235","messageId":"xmqqldo2lu7u.fsf@gitster.g","threadId":"63618","inReplyTo":"20250801131111.8115-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v7 5/5] repo: add the --format flag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-01T19:25:57Z","receivedAt":"2025-08-01T19:25:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add the --format flag to git-repo-info. By using this flag, the users\n> can choose the format for obtaining the data they requested.\n>\n> Given that this command can be used for generating input for other\n> applications and for being read by end users, it requires at least two\n> formats: one for being read by humans and other for being read by\n> machines. Some other Git commands also have two output formats, notably\n> git-config which was the inspiration for the two formats that were\n> chosen here:\n>\n> - keyvalue, where the retrieved data is printed one per line, using =\n>   for delimiting the key and the value. This is the default format,\n>   targeted for end users.\n> - nul, where the retrieved data is separated by null characters, using\n>   the newline character for delimiting the key and the value. This\n>   format is targeted for being read by machines.\n>\n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  Documentation/git-repo.adoc | 31 +++++++++++++++++++-\n>  builtin/repo.c              | 58 +++++++++++++++++++++++++++++++------\n>  t/t1900-repo.sh             | 21 ++++++++++++--\n>  3 files changed, 97 insertions(+), 13 deletions(-)\n\nThis will break t0450, won't it?\n"},{"id":"523239","messageId":"12714201.O9o76ZdvQC@cayenne","threadId":"63618","inReplyTo":"20250801131111.8115-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v7 5/5] repo: add the --format flag","fromName":"Jean-Noël AVILA","fromEmail":"jn.avila@free.fr","sentAt":"2025-08-01T20:27:27Z","receivedAt":"2025-08-01T20:35:08Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"On Friday, 1 August 2025 15:11:10 CEST Lucas Seiki Oshiro wrote:\n> Add the --format flag to git-repo-info. By using this flag, the users\n> can choose the format for obtaining the data they requested.\n> \n> Given that this command can be used for generating input for other\n> applications and for being read by end users, it requires at least two\n> formats: one for being read by humans and other for being read by\n> machines. Some other Git commands also have two output formats, notably\n> git-config which was the inspiration for the two formats that were\n> chosen here:\n> \n> - keyvalue, where the retrieved data is printed one per line, using =\n>   for delimiting the key and the value. This is the default format,\n>   targeted for end users.\n> - nul, where the retrieved data is separated by null characters, using\n>   the newline character for delimiting the key and the value. This\n>   format is targeted for being read by machines.\n> \n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Helped-by: Justin Tobler <jltobler@gmail.com>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Mentored-by: Karthik Nayak <karthik.188@gmail.com>\n> Mentored-by: Patrick Steinhardt <ps@pks.im>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n>  Documentation/git-repo.adoc | 31 +++++++++++++++++++-\n>  builtin/repo.c              | 58 +++++++++++++++++++++++++++++++------\n>  t/t1900-repo.sh             | 21 ++++++++++++--\n>  3 files changed, 97 insertions(+), 13 deletions(-)\n> \n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index 140ee3a0aa..b735cf4737 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -18,12 +18,23 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n> \n>  COMMANDS\n>  --------\n> -`info [<key>...]`::\n> +`info [--format=<keyvalue|nul>] [<key>...]`::\n\nAccording to the source code, I understand that this line should read:\n\n`info [--format=(keyvalue|nul)] [<key>...]`::\n\nWith parentheses, which is that `keyvalue` and `nul` are keywords describing \nthe type of format.\n\n>  \tRetrieve metadata-related information about the current \nrepository. Only\n>  \tthe requested data will be returned based on their keys (see \"INFO \nKEYS\"\n>  \tsection below).\n>  +\n>  The returned data is lexicographically sorted by the keys.\n> ++\n> +The output format can be chosen through the flag `--format`. Two formats are\n> +supported:\n> ++\n> +* `keyvalue`: output key-value pairs one per line using the `=` character \nas\n> +the delimiter between the key and the value. This is the default.\n> +\n> +* `nul`: similar to `keyvalue`, but using a newline character as the \ndelimiter\n> +between the key and the value and using a null character after each value.\n> +This format is better suited for being parsed by another applications than\n> +`keyvalue`.\n> \n>  INFO KEYS\n>  ---------\n> @@ -43,6 +54,24 @@ The reference storage format. The valid values are:\n>  +\n>  include::ref-storage-format.adoc[]\n> \n> +\n> +Examples\n> +--------\n> +\n> +* Retrieves the reference format of the current repository:\n> ++\n> +------------\n> +git repo info references.format\n> +------------\n> ++\n> +\n> +* Retrieves whether the current repository is bare and whether it is \nshallow\n> +using the `nul` format:\n> ++\n> +------------\n> +git repo info --format=nul layout.bare layout.shallow\n> +------------\n> +\n>  SEE ALSO\n>  --------\n>  linkgit:git-rev-parse[1]\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index 56c3a4027f..4015cf88b7 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -9,12 +9,17 @@\n>  #include \"shallow.h\"\n> \n>  static const char *const repo_usage[] = {\n> -\t\"git repo info [<key>...]\",\n> +\t\"git repo info [--format=<keyvalue|nul>] [<key>...]\",\n>  \tNULL\n>  };\n> \n>  typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n> \n> +enum output_format {\n> +\tFORMAT_KEYVALUE,\n> +\tFORMAT_NUL_TERMINATED,\n> +};\n> +\n>  struct field {\n>  \tconst char *key;\n>  \tget_value_fn *get_value;\n> @@ -74,18 +79,33 @@ static int qsort_strcmp(const void *va, const void *vb)\n>  \treturn strcmp(a, b);\n>  }\n> \n> -static int print_fields(int argc, const char **argv, struct repository \n*repo)\n> +static int print_fields(int argc, const char **argv,\n> +\t\t\tstruct repository *repo,\n> +\t\t\tenum output_format format)\n>  {\n>  \tint ret = 0;\n>  \tconst char *last = \"\";\n>  \tstruct strbuf sb = STRBUF_INIT;\n> \n> +\tchar kv_sep;\n> +\tchar field_sep;\n> +\n> +\tswitch (format) {\n> +\tcase FORMAT_KEYVALUE:\n> +\t\tkv_sep = '=';\n> +\t\tfield_sep = '\\n';\n> +\t\tbreak;\n> +\tcase FORMAT_NUL_TERMINATED:\n> +\t\tkv_sep = '\\n';\n> +\t\tfield_sep = '\\0';\n> +\t\tbreak;\n> +\t}\n> +\n>  \tQSORT(argv, argc, qsort_strcmp);\n> \n>  \tfor (int i = 0; i < argc; i++) {\n>  \t\tget_value_fn *get_value;\n>  \t\tconst char *key = argv[i];\n> -\t\tchar *value;\n> \n>  \t\tif (!strcmp(key, last))\n>  \t\t\tcontinue;\n> @@ -100,11 +120,14 @@ static int print_fields(int argc, const char **argv, \nstruct\n> repository *repo) strbuf_reset(&sb);\n>  \t\tget_value(repo, &sb);\n> \n> -\t\tvalue = strbuf_detach(&sb, NULL);\n> -\t\tquote_c_style(value, &sb, NULL, 0);\n> -\t\tfree(value);\n> +\t\tif (format == FORMAT_KEYVALUE) {\n> +\t\t\tchar *value;\n> +\t\t\tvalue = strbuf_detach(&sb, NULL);\n> +\t\t\tquote_c_style(value, &sb, NULL, 0);\n> +\t\t\tfree(value);\n> +\t\t}\n> \n> -\t\tprintf(\"%s=%s\\n\", key, sb.buf);\n> +\t\tprintf(\"%s%c%s%c\", key, kv_sep, sb.buf, field_sep);\n>  \t\tlast = key;\n>  \t}\n> \n> @@ -112,10 +135,27 @@ static int print_fields(int argc, const char **argv, \nstruct\n> repository *repo) return ret;\n>  }\n> \n> -static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n> +static int repo_info(int argc, const char **argv, const char *prefix,\n>  \t\t     struct repository *repo)\n>  {\n> -\treturn print_fields(argc - 1, argv + 1, repo);\n> +\tconst char *format_str = \"keyvalue\";\n> +\tenum output_format format;\n> +\tstruct option options[] = {\n> +\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n> +\t\t\t   N_(\"output format\")),\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n> +\n> +\tif (!strcmp(format_str, \"keyvalue\"))\n> +\t\tformat = FORMAT_KEYVALUE;\n> +\telse if (!strcmp(format_str, \"nul\"))\n> +\t\tformat = FORMAT_NUL_TERMINATED;\n> +\telse\n> +\t\tdie(_(\"invalid format '%s'\"), format_str);\n> +\n> +\treturn print_fields(argc, argv, repo, format);\n>  }\n> \n>  int cmd_repo(int argc, const char **argv, const char *prefix,\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> index 6706cb4c44..463ec3af92 100755\n> --- a/t/t1900-repo.sh\n> +++ b/t/t1900-repo.sh\n> @@ -21,10 +21,17 @@ test_repo_info () {\n>  \tkey=$4\n>  \texpected_value=$5\n> \n> -\ttest_expect_success \"$label\" '\n> -\t\teval \"$init_command $repo_name\" &&\n> +\ttest_expect_success \"keyvalue: $label\" '\n> +\t\teval \"$init_command keyvalue-$repo_name\" &&\n>  \t\techo \"$key=$expected_value\" >expected &&\n> -\t\tgit -C $repo_name repo info \"$key\" >actual &&\n> +\t\tgit -C keyvalue-$repo_name repo info \"$key\" >actual &&\n> +\t\ttest_cmp expected actual\n> +\t'\n> +\n> +\ttest_expect_success \"nul: $label\" '\n> +\t\teval \"$init_command nul-$repo_name\" &&\n> +\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n> +\t\tgit -C nul-$repo_name repo info --format=nul \"$key\" \n>actual &&\n>  \t\ttest_cmp expected actual\n>  \t'\n>  }\n> @@ -45,6 +52,7 @@ test_repo_info 'shallow repository = false is retrieved\n> correctly' ' git init' 'nonshallow' 'layout.shallow' 'false'\n> \n>  test_repo_info 'shallow repository = true is retrieved correctly' '\n> +\ttest_when_finished \"rm -rf remote\" &&\n>  \tgit init remote &&\n>  \techo x >remote/x &&\n>  \tgit -C remote add x &&\n> @@ -79,4 +87,11 @@ test_expect_success 'output is returned correctly when \ntwo keys\n> are requested' ' git -C two-keys repo info layout.bare references.format\n>  '\n> \n> +test_expect_success 'git-repo-info aborts when requesting an invalid \nformat' '\n> +\ttest_when_finished \"rm -f err expected\" &&\n> +\techo \"fatal: invalid format '\\'foo\\''\" >expected &&\n> +\ttest_must_fail git repo info --format=foo 2>err &&\n> +\ttest_cmp expected err\n> +'\n> +\n>  test_done\n\n\n\n\n"},{"id":"523241","messageId":"CAPig+cTxNUPayO2SdCL-BPtjb2rfr3e3RK=BsQxAiiEAtpBaRg@mail.gmail.com","threadId":"63618","inReplyTo":"20250801131111.8115-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v7 2/5] repo: add the field references.format","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-01T20:59:15Z","receivedAt":"2025-08-01T20:59:27Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Aug 1, 2025 at 9:11 AM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> This commit is part of the series that introduces the new subcommand\n> git-repo-info.\n> [...]\n> Add a new field `references.format` to the repo-info subcommand\n> containing that information.\n>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> @@ -22,6 +22,20 @@ COMMANDS\n> +In order to obtain a set of values from `git repo info`, you should provide\n> +the keys that identify them. Here's a list of the available keys and the\n> +values that they return:\n> +\n> +`references.format`::\n> +The reference storage format. The valid values are:\n> ++\n> +include::ref-storage-format.adoc[]\n\nIn the implementation below, I see that this version of the series\npasses all the printed values through quote_c_style(), which is a\nwelcome change, however, an equally (if not more) important change\nseems to be missing. Namely, we _must_ document that values with\n\"funny\" characters will be C-style quoted. Without such documentation,\nconsumers are left in the same sort of situation as they were in\nwithout quote_c_style(); to wit, they will be surprised and their\ntooling may break when they suddenly encounter a value which is\nquoted.\n\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> +static int print_fields(int argc, const char **argv, struct repository *repo)\n> +{\n> +       int ret = 0;\n> +       const char *last = \"\";\n> +       struct strbuf sb = STRBUF_INIT;\n> +\n> +       QSORT(argv, argc, qsort_strcmp);\n> +\n> +       for (int i = 0; i < argc; i++) {\n> +               get_value_fn *get_value;\n> +               const char *key = argv[i];\n> +               char *value;\n> +\n> +               if (!strcmp(key, last))\n> +                       continue;\n> +\n> +               get_value = get_value_fn_for_key(key);\n> +\n> +               if (!get_value) {\n> +                       ret = error(_(\"key '%s' not found\"), key);\n> +                       continue;\n> +               }\n> +\n> +               strbuf_reset(&sb);\n> +               get_value(repo, &sb);\n> +\n> +               value = strbuf_detach(&sb, NULL);\n> +               quote_c_style(value, &sb, NULL, 0);\n> +               free(value);\n> +\n> +               printf(\"%s=%s\\n\", key, sb.buf);\n> +               last = key;\n> +       }\n> +\n> +       strbuf_release(&sb);\n> +       return ret;\n> +}\n\nThis logic leads to a poor user-experience if the user asks for the\nsame non-existent key multiple times since that case subverts the\ndeduplication logic. For instance:\n\n    % git repo info non.existent references.format non.existent\n    key 'non.existent' not found\n    key 'non.existent' not found\n    references.format=gobbledygook\n\nYou can fix this by performing the `last` assignment earlier in the\nloop prior to any other `continue` statements:\n\n    if (!strcmp(key, last))\n        continue;\n    last = key;\n\n    get_value = get_value_fn_for_key(key);\n    if (!get_value) {\n        ret = error(_(\"key '%s' not found\"), key);\n        continue;\n    }\n\nThat aside, the strbuf detach/repurpose/free dance that the code does:\n\n    value = strbuf_detach(&sb, NULL);\n    quote_c_style(value, &sb, NULL, 0);\n    free(value);\n\nis unnecessarily confusing and difficult to fathom because it is\nrepurposing the strubuf and increasing the number of allocations and\ndeallocations for no apparent reason. You can decrease the cognitive\nload simply by using two strbufs, one for each distinct purpose,\nperhaps like this:\n\n    struct strbuf valbuf = STRBUF_INIT;\n    struct strbuf quotbuf = STRBUF_INIT;\n    ...\n    for (int i = 0; i < argc; i++) {\n        ...\n        strbuf_reset(&valbuf);\n        strbuf_reset(&quotbuf);\n        get_value(repo, &valbuf);\n        quote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n        printf(\"%s=%s\\n\", key, quotbuf.buf);\n    }\n    strbuf_release(&quotbuf);\n    strbuf_release(&valbuf);\n\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> @@ -0,0 +1,57 @@\n> +# Test whether a key-value pair is correctly returned\n> +#\n> +# Usage: test_repo_info <label> <init command> <key> <expected value>\n> +#\n> +# Arguments:\n> +#   label: the label of the test\n> +#   init command: a command which creates a repository named with its first argument,\n> +#      accordingly to what is being tested\n> +#   key: the key of the field that is being tested\n> +#   expected value: the value that the field should contain\n> +test_repo_info () {\n> +       label=$1\n> +       init_command=$2\n> +       repo_name=$3\n> +       key=$4\n> +       expected_value=$5\n\nThe function documentation (including \"Usage\") talks about four\narguments, but the function expects five.\n\nI'm having trouble understanding what is meant by \"repository named\nwith its first argument accordingly to what is being tested\". Also:\ns/accordingly/according/\n\n> +       test_expect_success \"$label\" '\n> +               eval \"$init_command $repo_name\" &&\n> +               echo \"$key=$expected_value\" >expected &&\n> +               git -C $repo_name repo info \"$key\" >actual &&\n> +               test_cmp expected actual\n> +       '\n> +}\n> +\n> +test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n> +       echo \"references.format=files\" >expected &&\n> +       test_must_fail git repo info foo references.format bar >actual &&\n> +       test_cmp expected actual\n> +'\n> +\n> +test_expect_success 'only one value is returned if the same key is requested twice' '\n> +       val=$(git rev-parse --show-ref-format) &&\n> +       echo \"references.format=$val\" >expect &&\n> +       git repo info references.format references.format >actual &&\n> +       test_cmp expect actual\n> +'\n\nThese tests are easier to understand and are more robust in this version. Good.\n"},{"id":"523243","messageId":"CAPig+cR=vRu7GwGx_wpS_GZNdX7giosDK12K+qQdOW1va-6oWw@mail.gmail.com","threadId":"63618","inReplyTo":"20250801131111.8115-4-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v7 3/5] repo: add the field layout.bare","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-01T21:21:41Z","receivedAt":"2025-08-01T21:21:53Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Aug 1, 2025 at 9:11 AM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> This commit is part of the series that introduces the new subcommand\n> git-repo-info.\n>\n> The flag --is-bare-repository from git-rev-parse is used for retrieving\n> whether the current repository is bare. This way, it is used for\n> querying repository metadata, fitting in the purpose of git-repo-info.\n>\n> Then, add a new field layout.bare to the git-repo-info subcommand\n> containing that information.\n>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> @@ -16,6 +19,13 @@ struct field {\n> +static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n> +{\n> +       strbuf_addstr(buf,\n> +                     is_bare_repository() ? \"true\" : \"false\");\n> +       return 0;\n> +}\n\nNit: You can drop the unnecessary line wrapping:\n\n    strbuf_addstr(buf, is_bare_repository() ? \"true\" : \"false\");\n\nBut don't re-roll just for this.\n\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> @@ -35,6 +35,12 @@ test_repo_info 'ref format files is retrieved correctly' '\n> +test_repo_info 'bare repository = false is retrieved correctly' '\n> +       git init' 'bare' 'layout.bare' 'false'\n> +\n> +test_repo_info 'bare repository = true is retrieved correctly' '\n> +       git init --bare' 'nonbare' 'layout.bare' 'true'\n\nThe quote placement used in these calls to `test_repo_info` is quite\nunusual and more than a little confusing. I'm guessing you did it this\nway to avoid having to use a backslash to continue the line or did it\nto mimic how `test_expect/fail` is called, but it makes the function\ncall more difficult to understand than it ought to be. Instead, call\nthe function in the more traditional way:\n\n    test_repo_info 'bare repository = true is retrieved correctly' \\\n        'git init --bare' 'nonbare' 'layout.bare' 'true'\n\nThis comment applies to the previous patch, as well, but I didn't\nnotice the issue when reviewing that patch.\n\n> @@ -54,4 +60,12 @@ test_expect_success 'only one value is returned if the same key is requested twi\n> +test_expect_success 'output is returned correctly when two keys are requested' '\n> +       cat >expect <<-\\EOF &&\n> +       layout.bare=false\n> +       references.format=files\n> +       EOF\n> +       git init --ref-format=files two-keys &&\n> +       git -C two-keys repo info layout.bare references.format\n> +'\n\nIt's good to see use of the heredoc as suggested in the previous\nreview, but isn't this test missing something important? Namely, it's\nnever comparing the actual output to the expected output; in fact,\nit's never even capturing the actual output.\n"},{"id":"523245","messageId":"CAPig+cTtJHn3NxrXF-AU_0cRzccDOo+j8JTAq1Jyqm4BzXBpSw@mail.gmail.com","threadId":"63618","inReplyTo":"20250801131111.8115-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v7 5/5] repo: add the --format flag","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-01T21:50:22Z","receivedAt":"2025-08-01T21:50:35Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Aug 1, 2025 at 9:11 AM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> Add the --format flag to git-repo-info. By using this flag, the users\n> can choose the format for obtaining the data they requested.\n>\n> Given that this command can be used for generating input for other\n> applications and for being read by end users, it requires at least two\n> formats: one for being read by humans and other for being read by\n> machines. Some other Git commands also have two output formats, notably\n> git-config which was the inspiration for the two formats that were\n> chosen here:\n>\n> - keyvalue, where the retrieved data is printed one per line, using =\n>   for delimiting the key and the value. This is the default format,\n>   targeted for end users.\n> - nul, where the retrieved data is separated by null characters, using\n>   the newline character for delimiting the key and the value. This\n>   format is targeted for being read by machines.\n>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> @@ -21,10 +21,17 @@ test_repo_info () {\n> -       test_expect_success \"$label\" '\n> -               eval \"$init_command $repo_name\" &&\n> +       test_expect_success \"keyvalue: $label\" '\n> +               eval \"$init_command keyvalue-$repo_name\" &&\n>                 echo \"$key=$expected_value\" >expected &&\n> -               git -C $repo_name repo info \"$key\" >actual &&\n> +               git -C keyvalue-$repo_name repo info \"$key\" >actual &&\n> +               test_cmp expected actual\n> +       '\n> +\n> +       test_expect_success \"nul: $label\" '\n> +               eval \"$init_command nul-$repo_name\" &&\n> +               printf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n> +               git -C nul-$repo_name repo info --format=nul \"$key\" >actual &&\n>                 test_cmp expected actual\n>         '\n\nDue to the embedded NUL's in this new \"nul\" test, I'm pretty sure you\nwant to be using `test_cmp_bin` here as suggested previously[*].\n\n[*]: https://lore.kernel.org/git/CAPig+cQn7c5+k06yHOD2jxYTGnny7is=fbo4tOw26eD+4zX-Jw@mail.gmail.com/\n\n> @@ -45,6 +52,7 @@ test_repo_info 'shallow repository = false is retrieved correctly' '\n>  test_repo_info 'shallow repository = true is retrieved correctly' '\n> +       test_when_finished \"rm -rf remote\" &&\n>         git init remote &&\n>         echo x >remote/x &&\n>         git -C remote add x &&\n\nFor what it's worth, it would be clearer to turn the removal of\n\"remote\" into a \"make sure we have a clean-slate for what we are about\nto do\" rather than making it an after-the-fact cleanup. That is:\n\n    test_repo_info 'shallow repository = true is retrieved correctly' '\n        rm -rf remote &&\n        git init remote &&\n        ...\n    '\n\nAlternatively, since the \"remote\" repository is static in the sense\nthat it is the same for both the \"keyvalue\" and \"nul\" cases, it would\nbe even clearer to just separate it out into its own \"setup\"-style\ntest:\n\n    test_expect_success 'setup remote' '\n        git init remote &&\n        echo x >remote/x &&\n        ...\n    '\n\n    test_repo_info 'shallow repository = true is retrieved correctly' \\\n       'git clone --depth 1 \"file://$PWD/remote\"' 'shallow'\n'layout.shallow' 'true'\n\n> @@ -79,4 +87,11 @@ test_expect_success 'output is returned correctly when two keys are requested' '\n> +test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n> +       test_when_finished \"rm -f err expected\" &&\n> +       echo \"fatal: invalid format '\\'foo\\''\" >expected &&\n> +       test_must_fail git repo info --format=foo 2>err &&\n> +       test_cmp expected err\n> +'\n\nDo we need to perform this `test_when_finished` cleanup? As mentioned\nin earlier reviews, we don't usually perform cleanup unnecessarily\nsince doing so slows down the test suite and makes it more difficult\nto debug a failing test.\n\nAlso, didn't patch [2/5] already add this exact test?\n"},{"id":"523361","messageId":"DC57657C-41E9-4DCA-AA7E-9DC5ADB2DC40@gmail.com","threadId":"63618","inReplyTo":"CAPig+cTxNUPayO2SdCL-BPtjb2rfr3e3RK=BsQxAiiEAtpBaRg@mail.gmail.com","subject":"Re: [GSoC PATCH v7 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-03T21:47:12Z","receivedAt":"2025-08-03T21:47:22Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"> This logic leads to a poor user-experience if the user asks for the\n> same non-existent key multiple times since that case subverts the\n> deduplication logic.\n\nMakes sense. I'll change it in v8.\n\n> is unnecessarily confusing and difficult to fathom because it is\n> repurposing the strubuf and increasing the number of allocations and\n> deallocations for no apparent reason.\n\nAgreed. Given that the strings that I'm storing are relatively small,\nit won't hurt to have two strbufs. I'll incorporate your suggestion.\n\n> The function documentation (including \"Usage\") talks about four\n> arguments, but the function expects five.\n\nI changed the parameters but I forgot to change the docs. I'll\nchange it in v8.\n\n> I'm having trouble understanding what is meant by \"repository named\n> with its first argument accordingly to what is being tested\".\n\nIndeed, this is confusing. Actually, this solution of using\n`eval \"$init_command $repo_name\"` is quite fragile. I'll change it\nto a more robust solution.\n\n> These tests are easier to understand and are more robust in this version. Good.\n\nThanks, Eric. Your reviews were really helpful for making these\ntests better!"},{"id":"523363","messageId":"DADE940E-482B-45DC-8020-BD422FBA7B2E@gmail.com","threadId":"63618","inReplyTo":"CAPig+cR=vRu7GwGx_wpS_GZNdX7giosDK12K+qQdOW1va-6oWw@mail.gmail.com","subject":"Re: [GSoC PATCH v7 3/5] repo: add the field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-03T22:54:22Z","receivedAt":"2025-08-03T22:54:38Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> The quote placement used in these calls to `test_repo_info` is quite\n> unusual and more than a little confusing. I'm guessing you did it this\n> way to avoid having to use a backslash to continue the line or did it\n> to mimic how `test_expect/fail` is called\n\nYeah, that's true :-). I'll change it. However, in the next patch, where\nI add `repository.shallow`, I need a more complex script for creating\na shallow repository which needs several lines. In this case, do you\nthink it would it be better to use the backslash before the line break\nor would it be like it currently is?\n\nThanks!\n"},{"id":"523364","messageId":"CAPig+cSqrmoC68Yc5OEb7oHS2V6YsWL-bfiPh7bHCQq-MV-fhQ@mail.gmail.com","threadId":"63618","inReplyTo":"DADE940E-482B-45DC-8020-BD422FBA7B2E@gmail.com","subject":"Re: [GSoC PATCH v7 3/5] repo: add the field layout.bare","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-03T23:06:23Z","receivedAt":"2025-08-03T23:06:35Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Aug 3, 2025 at 6:54 PM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> > The quote placement used in these calls to `test_repo_info` is quite\n> > unusual and more than a little confusing. I'm guessing you did it this\n> > way to avoid having to use a backslash to continue the line or did it\n> > to mimic how `test_expect/fail` is called\n>\n> Yeah, that's true :-). I'll change it. However, in the next patch, where\n> I add `repository.shallow`, I need a more complex script for creating\n> a shallow repository which needs several lines. In this case, do you\n> think it would it be better to use the backslash before the line break\n> or would it be like it currently is?\n\nIf you take the suggestion I made in my review[*] of that subsequent\npatch (specifically, create a separate \"setup\"-style test), then I'm\npretty sure you won't need to use the complex script for the function\nargument; it should collapse to a single line as shown in [*].\n\n[*]: https://lore.kernel.org/git/CAPig+cTtJHn3NxrXF-AU_0cRzccDOo+j8JTAq1Jyqm4BzXBpSw@mail.gmail.com/\n"},{"id":"523532","messageId":"aJH-BA0jIgSOJZTk@pks.im","threadId":"63618","inReplyTo":"20250801131111.8115-4-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v7 3/5] repo: add the field layout.bare","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-05T12:50:12Z","receivedAt":"2025-08-05T12:50:27Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Aug 01, 2025 at 10:11:08AM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> index 9796e36087..6c555e90c3 100755\n> --- a/t/t1900-repo.sh\n> +++ b/t/t1900-repo.sh\n> @@ -54,4 +60,12 @@ test_expect_success 'only one value is returned if the same key is requested twi\n>  \ttest_cmp expect actual\n>  '\n>  \n> +test_expect_success 'output is returned correctly when two keys are requested' '\n> +\tcat >expect <<-\\EOF &&\n> +\tlayout.bare=false\n> +\treferences.format=files\n> +\tEOF\n> +\tgit init --ref-format=files two-keys &&\n> +\tgit -C two-keys repo info layout.bare references.format\n> +'\n>  test_done\n\nTiny nit: there's a newline missing between the test and `test_done`.\n\nPatrick\n"},{"id":"523533","messageId":"aJH-FNHi5zaogs_k@pks.im","threadId":"63618","inReplyTo":"20250801131111.8115-5-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v7 4/5] repo: add the field layout.shallow","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-05T12:50:28Z","receivedAt":"2025-08-05T12:50:34Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Aug 01, 2025 at 10:11:09AM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index 2d51bfa195..56c3a4027f 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> index 6c555e90c3..6706cb4c44 100755\n> --- a/t/t1900-repo.sh\n> +++ b/t/t1900-repo.sh\n> @@ -68,4 +78,5 @@ test_expect_success 'output is returned correctly when two keys are requested' '\n>  \tgit init --ref-format=files two-keys &&\n>  \tgit -C two-keys repo info layout.bare references.format\n>  '\n> +\n>  test_done\n\nAh, you add the newline back in over here. You should move this fix into\nthe preceding commit.\n\nPatrick\n"},{"id":"523534","messageId":"aJH-GhcWyZLRV56m@pks.im","threadId":"63618","inReplyTo":"20250801131111.8115-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v7 5/5] repo: add the --format flag","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-05T12:50:34Z","receivedAt":"2025-08-05T12:50:40Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Aug 01, 2025 at 10:11:10AM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index 140ee3a0aa..b735cf4737 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> @@ -18,12 +18,23 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n>  \n>  COMMANDS\n>  --------\n> -`info [<key>...]`::\n> +`info [--format=<keyvalue|nul>] [<key>...]`::\n>  \tRetrieve metadata-related information about the current repository. Only\n>  \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n>  \tsection below).\n>  +\n>  The returned data is lexicographically sorted by the keys.\n> ++\n> +The output format can be chosen through the flag `--format`. Two formats are\n> +supported:\n> ++\n> +* `keyvalue`: output key-value pairs one per line using the `=` character as\n> +the delimiter between the key and the value. This is the default.\n\nLet's also mention that the value will be C-quoted.\n\n> +* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n> +between the key and the value and using a null character after each value.\n> +This format is better suited for being parsed by another applications than\n> +`keyvalue`.\n\nAnd here we can then explicitly say that it's not quoted.\n\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index 56c3a4027f..4015cf88b7 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -74,18 +79,33 @@ static int qsort_strcmp(const void *va, const void *vb)\n>  \treturn strcmp(a, b);\n>  }\n>  \n> -static int print_fields(int argc, const char **argv, struct repository *repo)\n> +static int print_fields(int argc, const char **argv,\n> +\t\t\tstruct repository *repo,\n> +\t\t\tenum output_format format)\n>  {\n>  \tint ret = 0;\n>  \tconst char *last = \"\";\n>  \tstruct strbuf sb = STRBUF_INIT;\n>  \n> +\tchar kv_sep;\n> +\tchar field_sep;\n\nNit: we don't usually have newlines between variable declarations.\n\nPatrick\n"},{"id":"523535","messageId":"aJH-IBe7w4mwGa2_@pks.im","threadId":"63618","inReplyTo":"20250801131111.8115-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v7 0/5] repo: add new command for retrieving repository info","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-05T12:50:40Z","receivedAt":"2025-08-05T12:50:46Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Aug 01, 2025 at 10:11:05AM -0300, Lucas Seiki Oshiro wrote:\n> Hi!\n> \n> These are the changes of this 7th version of `git repo`:\n> \n> - The tests were refactored (thanks Eric for your careful revision!)\n> \n> - The documentation has been improved: now it is a little more\n>   descriptive about the keys. I'm also including more information about\n>   the values\n> \n> - The documentation now contains examples\n> \n> - If an invalid key is requested, the command fails, however, now it\n>   returns all the valid fields that were requested\n> \n> - Now, I'm using `quote_c_style` in the key=value format\n> \n> Thanks!\n\nI think this series should be almost ready. I expect another final\nreroll to address the nits, but once those are addressed it should be\nready to go.\n\nThanks!\n\nPatrick\n"},{"id":"523671","messageId":"20250806195537.93302-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v8 0/5] repo: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-06T19:55:32Z","receivedAt":"2025-08-06T19:55:57Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi again!\n\nThis 8th version of `git-repo` basically address minor issues from the 7th\nversion:\n\n- A test wasn't passing in the CI because it assumed that the ref\n  format was files by default. I fixed that.\n\n- t0450 wasn't passing because the documentation didn't match the\n  usage string. I fixed that.\n\n- A test generated the output and the expected value but wasn't\n  comparing them. I also fixed that.\n\n- strbuf management: now we have two strbufs for printing values:\n  one for unquoted values and other for quoted values\n\n- Test codestyle: now it's ending test case liness with \\ instead of\n  open strings\n\n- Documentation: this version documents that we escape the value in\n  the `keyvalue` format.\n\nHere's the range-diff versus v7:\n\n1:  51b20490e2 = 1:  3c2ede66be repo: declare the repo command\n2:  8be77db9e5 ! 2:  396bee171a repo: add the field references.format\n    @@ Documentation/git-repo.adoc: COMMANDS\n      \tsection below).\n     ++\n     +The returned data is lexicographically sorted by the keys.\n    +++\n    ++The output format consists of key-value pairs one per line using the `=`\n    ++character as the delimiter between the key and the value. Values containing\n    ++\"unusual\" characters are quoted as explained for the configuration variable\n    ++`core.quotePath` (see linkgit:git-config[1]). This is the default.\n     +\n     +INFO KEYS\n     +---------\n    @@ builtin/repo.c\n     +{\n     +\tint ret = 0;\n     +\tconst char *last = \"\";\n    -+\tstruct strbuf sb = STRBUF_INIT;\n    ++\tstruct strbuf valbuf = STRBUF_INIT;\n    ++\tstruct strbuf quotbuf = STRBUF_INIT;\n     +\n     +\tQSORT(argv, argc, qsort_strcmp);\n     +\n     +\tfor (int i = 0; i < argc; i++) {\n     +\t\tget_value_fn *get_value;\n     +\t\tconst char *key = argv[i];\n    -+\t\tchar *value;\n    ++\n    ++\t\tstrbuf_reset(&valbuf);\n    ++\t\tstrbuf_reset(&quotbuf);\n     +\n     +\t\tif (!strcmp(key, last))\n     +\t\t\tcontinue;\n     +\n    ++\t\tlast = key;\n     +\t\tget_value = get_value_fn_for_key(key);\n     +\n     +\t\tif (!get_value) {\n    @@ builtin/repo.c\n     +\t\t\tcontinue;\n     +\t\t}\n     +\n    -+\t\tstrbuf_reset(&sb);\n    -+\t\tget_value(repo, &sb);\n    -+\n    -+\t\tvalue = strbuf_detach(&sb, NULL);\n    -+\t\tquote_c_style(value, &sb, NULL, 0);\n    -+\t\tfree(value);\n    -+\n    -+\t\tprintf(\"%s=%s\\n\", key, sb.buf);\n    -+\t\tlast = key;\n    ++\t\tget_value(repo, &valbuf);\n    ++\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n    ++\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n     +\t}\n     +\n    -+\tstrbuf_release(&sb);\n    ++\tstrbuf_release(&valbuf);\n    ++\tstrbuf_release(&quotbuf);\n     +\treturn ret;\n     +}\n     +\n    @@ t/t1900-repo.sh (new)\n     +#\n     +# Arguments:\n     +#   label: the label of the test\n    -+#   init command: a command which creates a repository named with its first argument,\n    -+#      accordingly to what is being tested\n    ++#   init_command: a command which creates a repository\n    ++#   repo_name: the name of the repository that will be created in init_command\n     +#   key: the key of the field that is being tested\n    -+#   expected value: the value that the field should contain\n    ++#   expected_value: the value that the field should contain\n     +test_repo_info () {\n     +\tlabel=$1\n     +\tinit_command=$2\n    @@ t/t1900-repo.sh (new)\n     +'\n     +\n     +test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n    -+\techo \"references.format=files\" >expected &&\n    ++\techo \"references.format=$(test_detect_ref_format)\" >expected &&\n     +\ttest_must_fail git repo info foo references.format bar >actual &&\n     +\ttest_cmp expected actual\n     +'\n3:  c93aeafb05 ! 3:  4dbc83c64c repo: add the field layout.bare\n    @@ builtin/repo.c: struct field {\n\n     +static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n     +{\n    -+\tstrbuf_addstr(buf,\n    -+\t\t      is_bare_repository() ? \"true\" : \"false\");\n    ++\tstrbuf_addstr(buf, is_bare_repository() ? \"true\" : \"false\");\n     +\treturn 0;\n     +}\n     +\n    @@ t/t1900-repo.sh: test_repo_info 'ref format files is retrieved correctly' '\n      test_repo_info 'ref format reftable is retrieved correctly' '\n      \tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n\n    -+test_repo_info 'bare repository = false is retrieved correctly' '\n    -+\tgit init' 'bare' 'layout.bare' 'false'\n    ++test_repo_info 'bare repository = false is retrieved correctly' \\\n    ++\t'git init' 'nonbare' 'layout.bare' 'false'\n     +\n    -+test_repo_info 'bare repository = true is retrieved correctly' '\n    -+\tgit init --bare' 'nonbare' 'layout.bare' 'true'\n    ++test_repo_info 'bare repository = true is retrieved correctly' \\\n    ++\t'git init --bare' 'bare' 'layout.bare' 'true'\n     +\n      test_expect_success 'git-repo-info fails if an invalid key is requested' '\n      \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n    @@ t/t1900-repo.sh: test_expect_success 'only one value is returned if the same key\n      '\n\n     +test_expect_success 'output is returned correctly when two keys are requested' '\n    -+\tcat >expect <<-\\EOF &&\n    ++\tcat >expected <<-\\EOF &&\n     +\tlayout.bare=false\n     +\treferences.format=files\n     +\tEOF\n     +\tgit init --ref-format=files two-keys &&\n    -+\tgit -C two-keys repo info layout.bare references.format\n    ++\tgit -C two-keys repo info layout.bare references.format > actual &&\n    ++\ttest_cmp expected actual\n     +'\n    ++\n      test_done\n4:  4463b85193 ! 4:  5c65a24df4 repo: add the field layout.shallow\n    @@ builtin/repo.c: static int get_references_format(struct repository *repo, struct\n\n\n      ## t/t1900-repo.sh ##\n    -@@ t/t1900-repo.sh: test_repo_info 'bare repository = false is retrieved correctly' '\n    - test_repo_info 'bare repository = true is retrieved correctly' '\n    - \tgit init --bare' 'nonbare' 'layout.bare' 'true'\n    +@@ t/t1900-repo.sh: test_repo_info 'bare repository = false is retrieved correctly' \\\n    + test_repo_info 'bare repository = true is retrieved correctly' \\\n    + \t'git init --bare' 'bare' 'layout.bare' 'true'\n\n    -+test_repo_info 'shallow repository = false is retrieved correctly' '\n    -+\tgit init' 'nonshallow' 'layout.shallow' 'false'\n    ++test_repo_info 'shallow repository = false is retrieved correctly' \\\n    ++\t'git init' 'nonshallow' 'layout.shallow' 'false'\n     +\n    -+test_repo_info 'shallow repository = true is retrieved correctly' '\n    -+\tgit init remote &&\n    ++test_repo_info 'shallow repository = true is retrieved correctly' \\\n    ++\t'git init remote &&\n     +\techo x >remote/x &&\n     +\tgit -C remote add x &&\n     +\tgit -C remote commit -m x &&\n    @@ t/t1900-repo.sh: test_repo_info 'bare repository = false is retrieved correctly'\n      test_expect_success 'git-repo-info fails if an invalid key is requested' '\n      \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n      \ttest_must_fail git repo info foo 2>actual_err &&\n    -@@ t/t1900-repo.sh: test_expect_success 'output is returned correctly when two keys are requested' '\n    - \tgit init --ref-format=files two-keys &&\n    - \tgit -C two-keys repo info layout.bare references.format\n    - '\n    -+\n    - test_done\n5:  90427acf54 ! 5:  dc8ea099f5 repo: add the --format flag\n    @@ Documentation/git-repo.adoc: git-repo - Retrieve information about the repositor\n      --------\n      [synopsis]\n     -git repo info [<key>...]\n    -+git repo info [--format=<keyvalue|nul>] [<key>...]\n    ++git repo info [--format=(keyvalue|nul)] [<key>...]\n\n      DESCRIPTION\n      -----------\n    @@ Documentation/git-repo.adoc: THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHAN\n      COMMANDS\n      --------\n     -`info [<key>...]`::\n    -+`info [--format=<keyvalue|nul>] [<key>...]`::\n    ++`info [--format=(keyvalue|nul)] [<key>...]`::\n      \tRetrieve metadata-related information about the current repository. Only\n      \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n      \tsection below).\n      +\n      The returned data is lexicographically sorted by the keys.\n    -++\n    + +\n    +-The output format consists of key-value pairs one per line using the `=`\n    +-character as the delimiter between the key and the value. Values containing\n    +-\"unusual\" characters are quoted as explained for the configuration variable\n     +The output format can be chosen through the flag `--format`. Two formats are\n     +supported:\n     ++\n     +* `keyvalue`: output key-value pairs one per line using the `=` character as\n    -+the delimiter between the key and the value. This is the default.\n    -+\n    ++the delimiter between the key and the value. Values containing \"unusual\"\n    ++characters are quoted as explained for the configuration variable\n    + `core.quotePath` (see linkgit:git-config[1]). This is the default.\n    +\n     +* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n     +between the key and the value and using a null character after each value.\n     +This format is better suited for being parsed by another applications than\n    -+`keyvalue`.\n    -\n    ++`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n    ++\n      INFO KEYS\n      ---------\n    +\n     @@ Documentation/git-repo.adoc: The reference storage format. The valid values are:\n      +\n      include::ref-storage-format.adoc[]\n    @@ builtin/repo.c\n\n      static const char *const repo_usage[] = {\n     -\t\"git repo info [<key>...]\",\n    -+\t\"git repo info [--format=<keyvalue|nul>] [<key>...]\",\n    ++\t\"git repo info [--format=(keyvalue|nul)] [<key>...]\",\n      \tNULL\n      };\n\n    @@ builtin/repo.c: static int qsort_strcmp(const void *va, const void *vb)\n      {\n      \tint ret = 0;\n      \tconst char *last = \"\";\n    - \tstruct strbuf sb = STRBUF_INIT;\n    +@@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repository *repo)\n    + \t\t}\n\n    -+\tchar kv_sep;\n    -+\tchar field_sep;\n    -+\n    -+\tswitch (format) {\n    -+\tcase FORMAT_KEYVALUE:\n    -+\t\tkv_sep = '=';\n    -+\t\tfield_sep = '\\n';\n    -+\t\tbreak;\n    -+\tcase FORMAT_NUL_TERMINATED:\n    -+\t\tkv_sep = '\\n';\n    -+\t\tfield_sep = '\\0';\n    -+\t\tbreak;\n    -+\t}\n    + \t\tget_value(repo, &valbuf);\n    +-\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n    +-\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n     +\n    - \tQSORT(argv, argc, qsort_strcmp);\n    -\n    - \tfor (int i = 0; i < argc; i++) {\n    - \t\tget_value_fn *get_value;\n    - \t\tconst char *key = argv[i];\n    --\t\tchar *value;\n    -\n    - \t\tif (!strcmp(key, last))\n    - \t\t\tcontinue;\n    -@@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repository *repo)\n    - \t\tstrbuf_reset(&sb);\n    - \t\tget_value(repo, &sb);\n    -\n    --\t\tvalue = strbuf_detach(&sb, NULL);\n    --\t\tquote_c_style(value, &sb, NULL, 0);\n    --\t\tfree(value);\n    -+\t\tif (format == FORMAT_KEYVALUE) {\n    -+\t\t\tchar *value;\n    -+\t\t\tvalue = strbuf_detach(&sb, NULL);\n    -+\t\t\tquote_c_style(value, &sb, NULL, 0);\n    -+\t\t\tfree(value);\n    ++\t\tswitch (format) {\n    ++\t\tcase FORMAT_KEYVALUE:\n    ++\t\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n    ++\t\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n    ++\t\t\tbreak;\n    ++\t\tcase FORMAT_NUL_TERMINATED:\n    ++\t\t\tprintf(\"%s\\n%s%c\", key, valbuf.buf, '\\0');\n    ++\t\t\tbreak;\n    ++\t\tdefault:\n    ++\t\t\tBUG(\"%d: not a valid output format\", format);\n     +\t\t}\n    -\n    --\t\tprintf(\"%s=%s\\n\", key, sb.buf);\n    -+\t\tprintf(\"%s%c%s%c\", key, kv_sep, sb.buf, field_sep);\n    - \t\tlast = key;\n      \t}\n\n    + \tstrbuf_release(&valbuf);\n     @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repository *repo)\n      \treturn ret;\n      }\n    @@ t/t1900-repo.sh: test_repo_info () {\n\n     -\ttest_expect_success \"$label\" '\n     -\t\teval \"$init_command $repo_name\" &&\n    -+\ttest_expect_success \"keyvalue: $label\" '\n    -+\t\teval \"$init_command keyvalue-$repo_name\" &&\n    - \t\techo \"$key=$expected_value\" >expected &&\n    +-\t\techo \"$key=$expected_value\" >expected &&\n     -\t\tgit -C $repo_name repo info \"$key\" >actual &&\n    -+\t\tgit -C keyvalue-$repo_name repo info \"$key\" >actual &&\n    -+\t\ttest_cmp expected actual\n    -+\t'\n    ++\trepo_name_keyvalue=\"$repo_name\"-keyvalue\n    ++\trepo_name_nul=\"$repo_name\"-nul\n     +\n    -+\ttest_expect_success \"nul: $label\" '\n    -+\t\teval \"$init_command nul-$repo_name\" &&\n    -+\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n    -+\t\tgit -C nul-$repo_name repo info --format=nul \"$key\" >actual &&\n    ++\ttest_expect_success \"keyvalue: $label\" '\n    ++\t\teval \"$init_command $repo_name_keyvalue\" &&\n    ++\t\techo \"$key=$expected_value\" > expected &&\n    ++\t\tgit -C \"$repo_name_keyvalue\" repo info \"$key\" >actual &&\n      \t\ttest_cmp expected actual\n      \t'\n    ++\n    ++\ttest_expect_success \"nul: $label\" '\n    ++\t\teval \"$init_command $repo_name_nul\" &&\n    ++\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n    ++\t\tgit -C \"$repo_name_nul\" repo info --format=nul \"$key\" >actual &&\n    ++\t\ttest_cmp_bin expected actual\n    ++\t'\n      }\n    -@@ t/t1900-repo.sh: test_repo_info 'shallow repository = false is retrieved correctly' '\n    - \tgit init' 'nonshallow' 'layout.shallow' 'false'\n\n    - test_repo_info 'shallow repository = true is retrieved correctly' '\n    -+\ttest_when_finished \"rm -rf remote\" &&\n    - \tgit init remote &&\n    + test_repo_info 'ref format files is retrieved correctly' '\n    +@@ t/t1900-repo.sh: test_repo_info 'bare repository = true is retrieved correctly' \\\n    + test_repo_info 'shallow repository = false is retrieved correctly' \\\n    + \t'git init' 'nonshallow' 'layout.shallow' 'false'\n    +\n    +-test_repo_info 'shallow repository = true is retrieved correctly' \\\n    +-\t'git init remote &&\n    ++test_expect_success 'setup remote' '\n    ++\tgit init remote &&\n      \techo x >remote/x &&\n      \tgit -C remote add x &&\n    +-\tgit -C remote commit -m x &&\n    +-\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n    ++\tgit -C remote commit -m x\n    ++'\n    ++\n    ++test_repo_info 'shallow repository = true is retrieved correctly' \\\n    ++\t'git clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n    +\n    + test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    + \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n     @@ t/t1900-repo.sh: test_expect_success 'output is returned correctly when two keys are requested' '\n    - \tgit -C two-keys repo info layout.bare references.format\n    + \ttest_cmp expected actual\n      '\n\n     +test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n    -+\ttest_when_finished \"rm -f err expected\" &&\n     +\techo \"fatal: invalid format '\\'foo\\''\" >expected &&\n     +\ttest_must_fail git repo info --format=foo 2>err &&\n     +\ttest_cmp expected err\n\nLucas Seiki Oshiro (5):\n  repo: declare the repo command\n  repo: add the field references.format\n  repo: add the field layout.bare\n  repo: add the field layout.shallow\n  repo: add the --format flag\n\n .gitignore                  |   1 +\n Documentation/git-repo.adoc |  81 ++++++++++++++++++\n Documentation/meson.build   |   1 +\n Makefile                    |   1 +\n builtin.h                   |   1 +\n builtin/repo.c              | 165 ++++++++++++++++++++++++++++++++++++\n command-list.txt            |   1 +\n git.c                       |   1 +\n meson.build                 |   1 +\n t/meson.build               |   1 +\n t/t1900-repo.sh             | 102 ++++++++++++++++++++++\n 11 files changed, 356 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n create mode 100755 t/t1900-repo.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523672","messageId":"20250806195537.93302-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250806195537.93302-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v8 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-06T19:55:33Z","receivedAt":"2025-08-06T19:56:01Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, `git rev-parse` covers a wide range of functionality not\ndirectly related to parsing revisions, as its name suggests. Over time,\nmany features like parsing datestrings, options, paths, and others\nwere added to it because there wasn't a more appropriate command\nto place them.\n\nCreate a new Git command called `repo`. `git repo` will be the main\ncommand for obtaining the information about a repository (such as\nmetadata and metrics).\n\nAlso declare a subcommand for `repo` called `info`. `git repo info`\nwill bring the functionality of retrieving repository-related\ninformation currently returned by `rev-parse`.\n\nAdd the required documentation and build changes to enable usage of\nthis subcommand.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore                  |  1 +\n Documentation/git-repo.adoc | 32 ++++++++++++++++++++++++++++++++\n Documentation/meson.build   |  1 +\n Makefile                    |  1 +\n builtin.h                   |  1 +\n builtin/repo.c              | 27 +++++++++++++++++++++++++++\n command-list.txt            |  1 +\n git.c                       |  1 +\n meson.build                 |  1 +\n 9 files changed, 66 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..1803023427 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nnew file mode 100644\nindex 0000000000..68c706f5a0\n--- /dev/null\n+++ b/Documentation/git-repo.adoc\n@@ -0,0 +1,32 @@\n+git-repo(1)\n+===========\n+\n+NAME\n+----\n+git-repo - Retrieve information about the repository\n+\n+SYNOPSIS\n+--------\n+[synopsis]\n+git repo info [<key>...]\n+\n+DESCRIPTION\n+-----------\n+Retrieve information about the repository.\n+\n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n+COMMANDS\n+--------\n+`info [<key>...]`::\n+\tRetrieve metadata-related information about the current repository. Only\n+\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n+\tsection below).\n+\n+SEE ALSO\n+--------\n+linkgit:git-rev-parse[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 4404c623f0..41f43e0336 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -116,6 +116,7 @@ manpages = {\n   'git-repack.adoc' : 1,\n   'git-replace.adoc' : 1,\n   'git-replay.adoc' : 1,\n+  'git-repo.adoc' : 1,\n   'git-request-pull.adoc' : 1,\n   'git-rerere.adoc' : 1,\n   'git-reset.adoc' : 1,\ndiff --git a/Makefile b/Makefile\nindex e11340c1ae..ec7ac58980 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1306,6 +1306,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..e6458e6fb9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo.c b/builtin/repo.c\nnew file mode 100644\nindex 0000000000..fd2a9b4216\n--- /dev/null\n+++ b/builtin/repo.c\n@@ -0,0 +1,27 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+static const char *const repo_usage[] = {\n+\t\"git repo info [<key>...]\",\n+\tNULL\n+};\n+\n+static int repo_info(int argc UNUSED, const char **argv UNUSED,\n+\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+{\n+\treturn 0;\n+}\n+\n+int cmd_repo(int argc, const char **argv, const char *prefix,\n+\t     struct repository *repo)\n+{\n+\tparse_opt_subcommand_fn *fn = NULL;\n+\tstruct option options[] = {\n+\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\treturn fn(argc, argv, prefix, repo);\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex b7ade3ab9f..1b0bdee00d 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -164,6 +164,7 @@ git-remote                              ancillarymanipulators           complete\n git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-replay                              plumbingmanipulators\n+git-repo                                plumbinginterrogators\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           history\ndiff --git a/git.c b/git.c\nindex 83eac0aeab..d4ff4d5517 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo\", cmd_repo, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 5dd299b496..e8ec0eca16 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523673","messageId":"20250806195537.93302-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250806195537.93302-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v8 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-06T19:55:34Z","receivedAt":"2025-08-06T19:56:06Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--show-ref-format` from git-rev-parse is used for retrieving\nthe reference format (i.e. `files` or `reftable`). This way, it is\nused for querying repository metadata, fitting in the purpose of\ngit-repo-info.\n\nAdd a new field `references.format` to the repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 19 ++++++++\n builtin/repo.c              | 89 ++++++++++++++++++++++++++++++++++++-\n t/meson.build               |  1 +\n t/t1900-repo.sh             | 57 ++++++++++++++++++++++++\n 4 files changed, 164 insertions(+), 2 deletions(-)\n create mode 100755 t/t1900-repo.sh\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 68c706f5a0..0ee783abc2 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -22,6 +22,25 @@ COMMANDS\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n++\n+The returned data is lexicographically sorted by the keys.\n++\n+The output format consists of key-value pairs one per line using the `=`\n+character as the delimiter between the key and the value. Values containing\n+\"unusual\" characters are quoted as explained for the configuration variable\n+`core.quotePath` (see linkgit:git-config[1]). This is the default.\n+\n+INFO KEYS\n+---------\n+\n+In order to obtain a set of values from `git repo info`, you should provide\n+the keys that identify them. Here's a list of the available keys and the\n+values that they return:\n+\n+`references.format`::\n+The reference storage format. The valid values are:\n++\n+include::ref-storage-format.adoc[]\n \n SEE ALSO\n --------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex fd2a9b4216..2b7ab5875e 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,17 +1,102 @@\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"quote.h\"\n+#include \"refs.h\"\n+#include \"strbuf.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n \tNULL\n };\n \n-static int repo_info(int argc UNUSED, const char **argv UNUSED,\n-\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n+\n+struct field {\n+\tconst char *key;\n+\tget_value_fn *get_value;\n+};\n+\n+static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n+\tstrbuf_addstr(buf,\n+\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n \treturn 0;\n }\n \n+/* repo_info_fields keys should be in lexicographical order */\n+static const struct field repo_info_fields[] = {\n+\t{ \"references.format\", get_references_format },\n+};\n+\n+static int repo_info_fields_cmp(const void *va, const void *vb)\n+{\n+\tconst struct field *a = va;\n+\tconst struct field *b = vb;\n+\n+\treturn strcmp(a->key, b->key);\n+}\n+\n+static get_value_fn *get_value_fn_for_key(const char *key)\n+{\n+\tconst struct field search_key = { key, NULL };\n+\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n+\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n+\t\t\t\t\t    sizeof(*found),\n+\t\t\t\t\t    repo_info_fields_cmp);\n+\treturn found ? found->get_value : NULL;\n+}\n+\n+static int qsort_strcmp(const void *va, const void *vb)\n+{\n+\tconst char *a = *(const char **)va;\n+\tconst char *b = *(const char **)vb;\n+\n+\treturn strcmp(a, b);\n+}\n+\n+static int print_fields(int argc, const char **argv, struct repository *repo)\n+{\n+\tint ret = 0;\n+\tconst char *last = \"\";\n+\tstruct strbuf valbuf = STRBUF_INIT;\n+\tstruct strbuf quotbuf = STRBUF_INIT;\n+\n+\tQSORT(argv, argc, qsort_strcmp);\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tget_value_fn *get_value;\n+\t\tconst char *key = argv[i];\n+\n+\t\tstrbuf_reset(&valbuf);\n+\t\tstrbuf_reset(&quotbuf);\n+\n+\t\tif (!strcmp(key, last))\n+\t\t\tcontinue;\n+\n+\t\tlast = key;\n+\t\tget_value = get_value_fn_for_key(key);\n+\n+\t\tif (!get_value) {\n+\t\t\tret = error(_(\"key '%s' not found\"), key);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tget_value(repo, &valbuf);\n+\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n+\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\t}\n+\n+\tstrbuf_release(&valbuf);\n+\tstrbuf_release(&quotbuf);\n+\treturn ret;\n+}\n+\n+static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+\t\t     struct repository *repo)\n+{\n+\treturn print_fields(argc - 1, argv + 1, repo);\n+}\n+\n int cmd_repo(int argc, const char **argv, const char *prefix,\n \t     struct repository *repo)\n {\ndiff --git a/t/meson.build b/t/meson.build\nindex bbeba1a8d5..252dbbc031 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -230,6 +230,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nnew file mode 100755\nindex 0000000000..ce02b394da\n--- /dev/null\n+++ b/t/t1900-repo.sh\n@@ -0,0 +1,57 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+\n+. ./test-lib.sh\n+\n+# Test whether a key-value pair is correctly returned\n+#\n+# Usage: test_repo_info <label> <init command> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init_command: a command which creates a repository\n+#   repo_name: the name of the repository that will be created in init_command\n+#   key: the key of the field that is being tested\n+#   expected_value: the value that the field should contain\n+test_repo_info () {\n+\tlabel=$1\n+\tinit_command=$2\n+\trepo_name=$3\n+\tkey=$4\n+\texpected_value=$5\n+\n+\ttest_expect_success \"$label\" '\n+\t\teval \"$init_command $repo_name\" &&\n+\t\techo \"$key=$expected_value\" >expected &&\n+\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n+\n+test_repo_info 'ref format files is retrieved correctly' '\n+\tgit init --ref-format=files' 'format-files' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' '\n+\tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n+\n+test_expect_success 'git-repo-info fails if an invalid key is requested' '\n+\techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n+\ttest_must_fail git repo info foo 2>actual_err &&\n+\ttest_cmp expected_err actual_err\n+'\n+\n+test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n+\techo \"references.format=$(test_detect_ref_format)\" >expected &&\n+\ttest_must_fail git repo info foo references.format bar >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'only one value is returned if the same key is requested twice' '\n+\tval=$(git rev-parse --show-ref-format) &&\n+\techo \"references.format=$val\" >expect &&\n+\tgit repo info references.format references.format >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523674","messageId":"20250806195537.93302-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250806195537.93302-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v8 3/5] repo: add the field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-06T19:55:35Z","receivedAt":"2025-08-06T19:56:10Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag --is-bare-repository from git-rev-parse is used for retrieving\nwhether the current repository is bare. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field layout.bare to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              | 10 ++++++++++\n t/t1900-repo.sh             | 16 ++++++++++++++++\n 3 files changed, 29 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 0ee783abc2..0ef851ee9c 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -37,6 +37,9 @@ In order to obtain a set of values from `git repo info`, you should provide\n the keys that identify them. Here's a list of the available keys and the\n values that they return:\n \n+`layout.bare`::\n+`true` if this is a bare repository, otherwise `false`.\n+\n `references.format`::\n The reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 2b7ab5875e..abdc929e19 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,4 +1,7 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n+#include \"environment.h\"\n #include \"parse-options.h\"\n #include \"quote.h\"\n #include \"refs.h\"\n@@ -16,6 +19,12 @@ struct field {\n \tget_value_fn *get_value;\n };\n \n+static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf, is_bare_repository() ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -25,6 +34,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n \n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n+\t{ \"layout.bare\", get_layout_bare },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex ce02b394da..32f64b635b 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -35,6 +35,12 @@ test_repo_info 'ref format files is retrieved correctly' '\n test_repo_info 'ref format reftable is retrieved correctly' '\n \tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' \\\n+\t'git init' 'nonbare' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' \\\n+\t'git init --bare' 'bare' 'layout.bare' 'true'\n+\n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n \ttest_must_fail git repo info foo 2>actual_err &&\n@@ -54,4 +60,14 @@ test_expect_success 'only one value is returned if the same key is requested twi\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'output is returned correctly when two keys are requested' '\n+\tcat >expected <<-\\EOF &&\n+\tlayout.bare=false\n+\treferences.format=files\n+\tEOF\n+\tgit init --ref-format=files two-keys &&\n+\tgit -C two-keys repo info layout.bare references.format > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523675","messageId":"20250806195537.93302-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250806195537.93302-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v8 4/5] repo: add the field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-06T19:55:36Z","receivedAt":"2025-08-06T19:56:14Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--is-shallow-repository` from git-rev-parse is used for\nretrieving whether the repository is shallow. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field `layout.shallow` to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              |  9 +++++++++\n t/t1900-repo.sh             | 10 ++++++++++\n 3 files changed, 22 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 0ef851ee9c..1ae9c09fac 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -40,6 +40,9 @@ values that they return:\n `layout.bare`::\n `true` if this is a bare repository, otherwise `false`.\n \n+`layout.shallow`::\n+`true` if this is a shallow repository, otherwise `false`.\n+\n `references.format`::\n The reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex abdc929e19..312fd08c34 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -6,6 +6,7 @@\n #include \"quote.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n+#include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n@@ -25,6 +26,13 @@ static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_layout_shallow(struct repository *repo, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf,\n+\t\t      is_repository_shallow(repo) ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -35,6 +43,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n \t{ \"layout.bare\", get_layout_bare },\n+\t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 32f64b635b..ef8205f7fd 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -41,6 +41,16 @@ test_repo_info 'bare repository = false is retrieved correctly' \\\n test_repo_info 'bare repository = true is retrieved correctly' \\\n \t'git init --bare' 'bare' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' \\\n+\t'git init' 'nonshallow' 'layout.shallow' 'false'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' \\\n+\t'git init remote &&\n+\techo x >remote/x &&\n+\tgit -C remote add x &&\n+\tgit -C remote commit -m x &&\n+\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n+\n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n \ttest_must_fail git repo info foo 2>actual_err &&\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523676","messageId":"20250806195537.93302-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250806195537.93302-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v8 5/5] repo: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-06T19:55:37Z","receivedAt":"2025-08-06T19:56:19Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the --format flag to git-repo-info. By using this flag, the users\ncan choose the format for obtaining the data they requested.\n\nGiven that this command can be used for generating input for other\napplications and for being read by end users, it requires at least two\nformats: one for being read by humans and other for being read by\nmachines. Some other Git commands also have two output formats, notably\ngit-config which was the inspiration for the two formats that were\nchosen here:\n\n- keyvalue, where the retrieved data is printed one per line, using =\n  for delimiting the key and the value. This is the default format,\n  targeted for end users.\n- nul, where the retrieved data is separated by null characters, using\n  the newline character for delimiting the key and the value. This\n  format is targeted for being read by machines.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 36 ++++++++++++++++++++++++-----\n builtin/repo.c              | 46 ++++++++++++++++++++++++++++++++-----\n t/t1900-repo.sh             | 35 +++++++++++++++++++++-------\n 3 files changed, 97 insertions(+), 20 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 1ae9c09fac..2b63954098 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -8,7 +8,7 @@ git-repo - Retrieve information about the repository\n SYNOPSIS\n --------\n [synopsis]\n-git repo info [<key>...]\n+git repo info [--format=(keyvalue|nul)] [<key>...]\n \n DESCRIPTION\n -----------\n@@ -18,21 +18,28 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n \n COMMANDS\n --------\n-`info [<key>...]`::\n+`info [--format=(keyvalue|nul)] [<key>...]`::\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n +\n The returned data is lexicographically sorted by the keys.\n +\n-The output format consists of key-value pairs one per line using the `=`\n-character as the delimiter between the key and the value. Values containing\n-\"unusual\" characters are quoted as explained for the configuration variable\n+The output format can be chosen through the flag `--format`. Two formats are\n+supported:\n++\n+* `keyvalue`: output key-value pairs one per line using the `=` character as\n+the delimiter between the key and the value. Values containing \"unusual\"\n+characters are quoted as explained for the configuration variable\n `core.quotePath` (see linkgit:git-config[1]). This is the default.\n \n+* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n+between the key and the value and using a null character after each value.\n+This format is better suited for being parsed by another applications than\n+`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n+\n INFO KEYS\n ---------\n-\n In order to obtain a set of values from `git repo info`, you should provide\n the keys that identify them. Here's a list of the available keys and the\n values that they return:\n@@ -48,6 +55,23 @@ The reference storage format. The valid values are:\n +\n include::ref-storage-format.adoc[]\n \n+EXAMPLES\n+--------\n+\n+* Retrieves the reference format of the current repository:\n++\n+------------\n+git repo info references.format\n+------------\n++\n+\n+* Retrieves whether the current repository is bare and whether it is shallow\n+using the `nul` format:\n++\n+------------\n+git repo info --format=nul layout.bare layout.shallow\n+------------\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 312fd08c34..37b5726816 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -9,12 +9,17 @@\n #include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n-\t\"git repo info [<key>...]\",\n+\t\"git repo info [--format=(keyvalue|nul)] [<key>...]\",\n \tNULL\n };\n \n typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n \n+enum output_format {\n+\tFORMAT_KEYVALUE,\n+\tFORMAT_NUL_TERMINATED,\n+};\n+\n struct field {\n \tconst char *key;\n \tget_value_fn *get_value;\n@@ -73,7 +78,9 @@ static int qsort_strcmp(const void *va, const void *vb)\n \treturn strcmp(a, b);\n }\n \n-static int print_fields(int argc, const char **argv, struct repository *repo)\n+static int print_fields(int argc, const char **argv,\n+\t\t\tstruct repository *repo,\n+\t\t\tenum output_format format)\n {\n \tint ret = 0;\n \tconst char *last = \"\";\n@@ -101,8 +108,18 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \t\t}\n \n \t\tget_value(repo, &valbuf);\n-\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n-\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\n+\t\tswitch (format) {\n+\t\tcase FORMAT_KEYVALUE:\n+\t\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n+\t\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\t\t\tbreak;\n+\t\tcase FORMAT_NUL_TERMINATED:\n+\t\t\tprintf(\"%s\\n%s%c\", key, valbuf.buf, '\\0');\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"%d: not a valid output format\", format);\n+\t\t}\n \t}\n \n \tstrbuf_release(&valbuf);\n@@ -110,10 +127,27 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \treturn ret;\n }\n \n-static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+static int repo_info(int argc, const char **argv, const char *prefix,\n \t\t     struct repository *repo)\n {\n-\treturn print_fields(argc - 1, argv + 1, repo);\n+\tconst char *format_str = \"keyvalue\";\n+\tenum output_format format;\n+\tstruct option options[] = {\n+\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n+\t\t\t   N_(\"output format\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\tif (!strcmp(format_str, \"keyvalue\"))\n+\t\tformat = FORMAT_KEYVALUE;\n+\telse if (!strcmp(format_str, \"nul\"))\n+\t\tformat = FORMAT_NUL_TERMINATED;\n+\telse\n+\t\tdie(_(\"invalid format '%s'\"), format_str);\n+\n+\treturn print_fields(argc, argv, repo, format);\n }\n \n int cmd_repo(int argc, const char **argv, const char *prefix,\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex ef8205f7fd..573cf62575 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -21,12 +21,22 @@ test_repo_info () {\n \tkey=$4\n \texpected_value=$5\n \n-\ttest_expect_success \"$label\" '\n-\t\teval \"$init_command $repo_name\" &&\n-\t\techo \"$key=$expected_value\" >expected &&\n-\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\trepo_name_keyvalue=\"$repo_name\"-keyvalue\n+\trepo_name_nul=\"$repo_name\"-nul\n+\n+\ttest_expect_success \"keyvalue: $label\" '\n+\t\teval \"$init_command $repo_name_keyvalue\" &&\n+\t\techo \"$key=$expected_value\" > expected &&\n+\t\tgit -C \"$repo_name_keyvalue\" repo info \"$key\" >actual &&\n \t\ttest_cmp expected actual\n \t'\n+\n+\ttest_expect_success \"nul: $label\" '\n+\t\teval \"$init_command $repo_name_nul\" &&\n+\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n+\t\tgit -C \"$repo_name_nul\" repo info --format=nul \"$key\" >actual &&\n+\t\ttest_cmp_bin expected actual\n+\t'\n }\n \n test_repo_info 'ref format files is retrieved correctly' '\n@@ -44,12 +54,15 @@ test_repo_info 'bare repository = true is retrieved correctly' \\\n test_repo_info 'shallow repository = false is retrieved correctly' \\\n \t'git init' 'nonshallow' 'layout.shallow' 'false'\n \n-test_repo_info 'shallow repository = true is retrieved correctly' \\\n-\t'git init remote &&\n+test_expect_success 'setup remote' '\n+\tgit init remote &&\n \techo x >remote/x &&\n \tgit -C remote add x &&\n-\tgit -C remote commit -m x &&\n-\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n+\tgit -C remote commit -m x\n+'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' \\\n+\t'git clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n \n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n@@ -80,4 +93,10 @@ test_expect_success 'output is returned correctly when two keys are requested' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n+\techo \"fatal: invalid format '\\'foo\\''\" >expected &&\n+\ttest_must_fail git repo info --format=foo 2>err &&\n+\ttest_cmp expected err\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523682","messageId":"xmqqbjost6sy.fsf@gitster.g","threadId":"63618","inReplyTo":"20250806195537.93302-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v8 0/5] repo: add new command for retrieving repository info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-06T22:38:05Z","receivedAt":"2025-08-06T22:38:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Hi again!\n>\n> This 8th version of `git-repo` basically address minor issues from the 7th\n> version:\n>\n> - A test wasn't passing in the CI because it assumed that the ref\n>   format was files by default. I fixed that.\n>\n> - t0450 wasn't passing because the documentation didn't match the\n>   usage string. I fixed that.\n>\n> - A test generated the output and the expected value but wasn't\n>   comparing them. I also fixed that.\n>\n> - strbuf management: now we have two strbufs for printing values:\n>   one for unquoted values and other for quoted values\n>\n> - Test codestyle: now it's ending test case liness with \\ instead of\n>   open strings\n>\n> - Documentation: this version documents that we escape the value in\n>   the `keyvalue` format.\n\nThanks for these updates.  Will replace and merge to 'seen'.\n"},{"id":"523703","messageId":"aJQ3sVf4MsgnCaMz@pks.im","threadId":"63618","inReplyTo":"20250806195537.93302-4-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v8 3/5] repo: add the field layout.bare","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-07T05:20:49Z","receivedAt":"2025-08-07T05:20:57Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Aug 06, 2025 at 04:55:35PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> index ce02b394da..32f64b635b 100755\n> --- a/t/t1900-repo.sh\n> +++ b/t/t1900-repo.sh\n> @@ -54,4 +60,14 @@ test_expect_success 'only one value is returned if the same key is requested twi\n>  \ttest_cmp expect actual\n>  '\n>  \n> +test_expect_success 'output is returned correctly when two keys are requested' '\n> +\tcat >expected <<-\\EOF &&\n> +\tlayout.bare=false\n> +\treferences.format=files\n> +\tEOF\n> +\tgit init --ref-format=files two-keys &&\n> +\tgit -C two-keys repo info layout.bare references.format > actual &&\n\nTiny nit, not worth a reroll on its own: our code style doesn't have a\nspace between the redirect operator and its target.\n\nPatrick\n"},{"id":"523704","messageId":"aJQ3tyC0e53iWtVE@pks.im","threadId":"63618","inReplyTo":"20250806195537.93302-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v8 5/5] repo: add the --format flag","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-07T05:20:55Z","receivedAt":"2025-08-07T05:21:02Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Aug 06, 2025 at 04:55:37PM -0300, Lucas Seiki Oshiro wrote:\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> index 1ae9c09fac..2b63954098 100644\n> --- a/Documentation/git-repo.adoc\n> +++ b/Documentation/git-repo.adoc\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index 312fd08c34..37b5726816 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -101,8 +108,18 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n>  \t\t}\n>  \n>  \t\tget_value(repo, &valbuf);\n> -\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n> -\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n> +\n> +\t\tswitch (format) {\n> +\t\tcase FORMAT_KEYVALUE:\n> +\t\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n> +\t\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n> +\t\t\tbreak;\n> +\t\tcase FORMAT_NUL_TERMINATED:\n> +\t\t\tprintf(\"%s\\n%s%c\", key, valbuf.buf, '\\0');\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tBUG(\"%d: not a valid output format\", format);\n\nNit: we typically say it the other way round.\n\n\tBUG(\"not a valid output format: %d\", format);\n\nDoesn't matter too much though as ideally this message shouldn't ever be\nseen by any user out there.\n\nOther than my two nits the series looks good to me, thanks! I don't\nterribly mind whether or not those nits are addressed.\n\nPatrick\n"},{"id":"523712","messageId":"CAOLa=ZSX0hFt7PRdXssz2xGG17bmDchS=EheBSmQj9xr+r_baA@mail.gmail.com","threadId":"63618","inReplyTo":"20250806195537.93302-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v8 2/5] repo: add the field references.format","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-08-07T07:43:53Z","receivedAt":"2025-08-07T07:43:54Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> new file mode 100755\n> index 0000000000..ce02b394da\n> --- /dev/null\n> +++ b/t/t1900-repo.sh\n> @@ -0,0 +1,57 @@\n> +#!/bin/sh\n> +\n> +test_description='test git repo-info'\n> +\n> +. ./test-lib.sh\n> +\n> +# Test whether a key-value pair is correctly returned\n> +#\n> +# Usage: test_repo_info <label> <init command> <key> <expected value>\n> +#\n> +# Arguments:\n> +#   label: the label of the test\n> +#   init_command: a command which creates a repository\n> +#   repo_name: the name of the repository that will be created in init_command\n> +#   key: the key of the field that is being tested\n> +#   expected_value: the value that the field should contain\n> +test_repo_info () {\n> +\tlabel=$1\n> +\tinit_command=$2\n> +\trepo_name=$3\n> +\tkey=$4\n> +\texpected_value=$5\n> +\n> +\ttest_expect_success \"$label\" '\n> +\t\teval \"$init_command $repo_name\" &&\n> +\t\techo \"$key=$expected_value\" >expected &&\n> +\t\tgit -C $repo_name repo info \"$key\" >actual &&\n> +\t\ttest_cmp expected actual\n> +\t'\n> +}\n> +\n> +test_repo_info 'ref format files is retrieved correctly' '\n> +\tgit init --ref-format=files' 'format-files' 'references.format' 'files'\n> +\n> +test_repo_info 'ref format reftable is retrieved correctly' '\n> +\tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n> +\n> +test_expect_success 'git-repo-info fails if an invalid key is requested' '\n> +\techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n\nNit: we generally use '${SQ}foo${SQ}' for single quoting in tests.\n\n> +\ttest_must_fail git repo info foo 2>actual_err &&\n> +\ttest_cmp expected_err actual_err\n> +'\n> +\n> +test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n> +\techo \"references.format=$(test_detect_ref_format)\" >expected &&\n> +\ttest_must_fail git repo info foo references.format bar >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'only one value is returned if the same key is requested twice' '\n> +\tval=$(git rev-parse --show-ref-format) &&\n> +\techo \"references.format=$val\" >expect &&\n> +\tgit repo info references.format references.format >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_done\n> --\n> 2.39.5 (Apple Git-154)\n"},{"id":"523713","messageId":"CAOLa=ZT9BvAp_TvD3MnUJxtpmT=U=hzi3NapokWbEyvyrv_y3A@mail.gmail.com","threadId":"63618","inReplyTo":"20250806195537.93302-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v8 0/5] repo: add new command for retrieving repository info","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-08-07T07:48:35Z","receivedAt":"2025-08-07T07:48:36Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Hi again!\n>\n> This 8th version of `git-repo` basically address minor issues from the 7th\n> version:\n>\n> - A test wasn't passing in the CI because it assumed that the ref\n>   format was files by default. I fixed that.\n>\n> - t0450 wasn't passing because the documentation didn't match the\n>   usage string. I fixed that.\n>\n> - A test generated the output and the expected value but wasn't\n>   comparing them. I also fixed that.\n>\n> - strbuf management: now we have two strbufs for printing values:\n>   one for unquoted values and other for quoted values\n>\n> - Test codestyle: now it's ending test case liness with \\ instead of\n>   open strings\n>\n> - Documentation: this version documents that we escape the value in\n>   the `keyvalue` format.\n>\n\nApart from a single nit on my side, I couldn't find any other changes\nneeded, this version looks good! :)\n\n[snip]\n"},{"id":"523751","messageId":"20250807150239.6987-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v9 0/5] repo: add new command for retrieving repository info","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-07T15:02:34Z","receivedAt":"2025-08-07T15:03:58Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\nThank you all for your time reviewing and helping me with this patchset!\n\nThis v9 only solve tiny nitpicks pointed by Karthik and Patrick in v8,\njust to make clear that everything is ok!\n\nJunio, would you mind to replace v8 by this v9 as lo/repo-info in seen?\n\nHere's the range-diff:\n\n1:  3c2ede66be = 1:  3c2ede66be repo: declare the repo command\n2:  396bee171a ! 2:  b18e74763d repo: add the field references.format\n    @@ t/t1900-repo.sh (new)\n     +\tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n     +\n     +test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    -+\techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n    ++\techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n     +\ttest_must_fail git repo info foo 2>actual_err &&\n     +\ttest_cmp expected_err actual_err\n     +'\n3:  4dbc83c64c ! 3:  35916b210e repo: add the field layout.bare\n    @@ t/t1900-repo.sh: test_repo_info 'ref format files is retrieved correctly' '\n     +\t'git init --bare' 'bare' 'layout.bare' 'true'\n     +\n      test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    - \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n    + \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n      \ttest_must_fail git repo info foo 2>actual_err &&\n     @@ t/t1900-repo.sh: test_expect_success 'only one value is returned if the same key is requested twi\n      \ttest_cmp expect actual\n    @@ t/t1900-repo.sh: test_expect_success 'only one value is returned if the same key\n     +\treferences.format=files\n     +\tEOF\n     +\tgit init --ref-format=files two-keys &&\n    -+\tgit -C two-keys repo info layout.bare references.format > actual &&\n    ++\tgit -C two-keys repo info layout.bare references.format >actual &&\n     +\ttest_cmp expected actual\n     +'\n     +\n4:  5c65a24df4 ! 4:  91fc5c4e50 repo: add the field layout.shallow\n    @@ t/t1900-repo.sh: test_repo_info 'bare repository = false is retrieved correctly'\n     +\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n     +\n      test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    - \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n    + \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n      \ttest_must_fail git repo info foo 2>actual_err &&\n5:  923b491324 ! 5:  8af32d7066 repo: add the --format flag\n    @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repo\n     +\t\t\tprintf(\"%s\\n%s%c\", key, valbuf.buf, '\\0');\n     +\t\t\tbreak;\n     +\t\tdefault:\n    -+\t\t\tBUG(\"%d: not a valid output format\", format);\n    ++\t\t\tBUG(\"not a valid output format: %d\", format);\n     +\t\t}\n      \t}\n\n    @@ t/t1900-repo.sh: test_repo_info 'bare repository = true is retrieved correctly'\n     +\t'git clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n\n      test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    - \techo \"error: key '\\'foo\\'' not found\" >expected_err &&\n    + \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n     @@ t/t1900-repo.sh: test_expect_success 'output is returned correctly when two keys are requested' '\n      \ttest_cmp expected actual\n      '\n\nLucas Seiki Oshiro (5):\n  repo: declare the repo command\n  repo: add the field references.format\n  repo: add the field layout.bare\n  repo: add the field layout.shallow\n  repo: add the --format flag\n\n .gitignore                  |   1 +\n Documentation/git-repo.adoc |  81 ++++++++++++++++++\n Documentation/meson.build   |   1 +\n Makefile                    |   1 +\n builtin.h                   |   1 +\n builtin/repo.c              | 165 ++++++++++++++++++++++++++++++++++++\n command-list.txt            |   1 +\n git.c                       |   1 +\n meson.build                 |   1 +\n t/meson.build               |   1 +\n t/t1900-repo.sh             | 102 ++++++++++++++++++++++\n 11 files changed, 356 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n create mode 100755 t/t1900-repo.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523752","messageId":"20250807150239.6987-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v9 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-07T15:02:35Z","receivedAt":"2025-08-07T15:04:01Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, `git rev-parse` covers a wide range of functionality not\ndirectly related to parsing revisions, as its name suggests. Over time,\nmany features like parsing datestrings, options, paths, and others\nwere added to it because there wasn't a more appropriate command\nto place them.\n\nCreate a new Git command called `repo`. `git repo` will be the main\ncommand for obtaining the information about a repository (such as\nmetadata and metrics).\n\nAlso declare a subcommand for `repo` called `info`. `git repo info`\nwill bring the functionality of retrieving repository-related\ninformation currently returned by `rev-parse`.\n\nAdd the required documentation and build changes to enable usage of\nthis subcommand.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore                  |  1 +\n Documentation/git-repo.adoc | 32 ++++++++++++++++++++++++++++++++\n Documentation/meson.build   |  1 +\n Makefile                    |  1 +\n builtin.h                   |  1 +\n builtin/repo.c              | 27 +++++++++++++++++++++++++++\n command-list.txt            |  1 +\n git.c                       |  1 +\n meson.build                 |  1 +\n 9 files changed, 66 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..1803023427 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nnew file mode 100644\nindex 0000000000..68c706f5a0\n--- /dev/null\n+++ b/Documentation/git-repo.adoc\n@@ -0,0 +1,32 @@\n+git-repo(1)\n+===========\n+\n+NAME\n+----\n+git-repo - Retrieve information about the repository\n+\n+SYNOPSIS\n+--------\n+[synopsis]\n+git repo info [<key>...]\n+\n+DESCRIPTION\n+-----------\n+Retrieve information about the repository.\n+\n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n+COMMANDS\n+--------\n+`info [<key>...]`::\n+\tRetrieve metadata-related information about the current repository. Only\n+\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n+\tsection below).\n+\n+SEE ALSO\n+--------\n+linkgit:git-rev-parse[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 4404c623f0..41f43e0336 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -116,6 +116,7 @@ manpages = {\n   'git-repack.adoc' : 1,\n   'git-replace.adoc' : 1,\n   'git-replay.adoc' : 1,\n+  'git-repo.adoc' : 1,\n   'git-request-pull.adoc' : 1,\n   'git-rerere.adoc' : 1,\n   'git-reset.adoc' : 1,\ndiff --git a/Makefile b/Makefile\nindex e11340c1ae..ec7ac58980 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1306,6 +1306,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..e6458e6fb9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo.c b/builtin/repo.c\nnew file mode 100644\nindex 0000000000..fd2a9b4216\n--- /dev/null\n+++ b/builtin/repo.c\n@@ -0,0 +1,27 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+static const char *const repo_usage[] = {\n+\t\"git repo info [<key>...]\",\n+\tNULL\n+};\n+\n+static int repo_info(int argc UNUSED, const char **argv UNUSED,\n+\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+{\n+\treturn 0;\n+}\n+\n+int cmd_repo(int argc, const char **argv, const char *prefix,\n+\t     struct repository *repo)\n+{\n+\tparse_opt_subcommand_fn *fn = NULL;\n+\tstruct option options[] = {\n+\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\treturn fn(argc, argv, prefix, repo);\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex b7ade3ab9f..1b0bdee00d 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -164,6 +164,7 @@ git-remote                              ancillarymanipulators           complete\n git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-replay                              plumbingmanipulators\n+git-repo                                plumbinginterrogators\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           history\ndiff --git a/git.c b/git.c\nindex 83eac0aeab..d4ff4d5517 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo\", cmd_repo, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 5dd299b496..e8ec0eca16 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523753","messageId":"20250807150239.6987-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v9 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-07T15:02:36Z","receivedAt":"2025-08-07T15:04:06Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--show-ref-format` from git-rev-parse is used for retrieving\nthe reference format (i.e. `files` or `reftable`). This way, it is\nused for querying repository metadata, fitting in the purpose of\ngit-repo-info.\n\nAdd a new field `references.format` to the repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 19 ++++++++\n builtin/repo.c              | 89 ++++++++++++++++++++++++++++++++++++-\n t/meson.build               |  1 +\n t/t1900-repo.sh             | 57 ++++++++++++++++++++++++\n 4 files changed, 164 insertions(+), 2 deletions(-)\n create mode 100755 t/t1900-repo.sh\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 68c706f5a0..0ee783abc2 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -22,6 +22,25 @@ COMMANDS\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n++\n+The returned data is lexicographically sorted by the keys.\n++\n+The output format consists of key-value pairs one per line using the `=`\n+character as the delimiter between the key and the value. Values containing\n+\"unusual\" characters are quoted as explained for the configuration variable\n+`core.quotePath` (see linkgit:git-config[1]). This is the default.\n+\n+INFO KEYS\n+---------\n+\n+In order to obtain a set of values from `git repo info`, you should provide\n+the keys that identify them. Here's a list of the available keys and the\n+values that they return:\n+\n+`references.format`::\n+The reference storage format. The valid values are:\n++\n+include::ref-storage-format.adoc[]\n \n SEE ALSO\n --------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex fd2a9b4216..2b7ab5875e 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,17 +1,102 @@\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"quote.h\"\n+#include \"refs.h\"\n+#include \"strbuf.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n \tNULL\n };\n \n-static int repo_info(int argc UNUSED, const char **argv UNUSED,\n-\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n+\n+struct field {\n+\tconst char *key;\n+\tget_value_fn *get_value;\n+};\n+\n+static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n+\tstrbuf_addstr(buf,\n+\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n \treturn 0;\n }\n \n+/* repo_info_fields keys should be in lexicographical order */\n+static const struct field repo_info_fields[] = {\n+\t{ \"references.format\", get_references_format },\n+};\n+\n+static int repo_info_fields_cmp(const void *va, const void *vb)\n+{\n+\tconst struct field *a = va;\n+\tconst struct field *b = vb;\n+\n+\treturn strcmp(a->key, b->key);\n+}\n+\n+static get_value_fn *get_value_fn_for_key(const char *key)\n+{\n+\tconst struct field search_key = { key, NULL };\n+\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n+\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n+\t\t\t\t\t    sizeof(*found),\n+\t\t\t\t\t    repo_info_fields_cmp);\n+\treturn found ? found->get_value : NULL;\n+}\n+\n+static int qsort_strcmp(const void *va, const void *vb)\n+{\n+\tconst char *a = *(const char **)va;\n+\tconst char *b = *(const char **)vb;\n+\n+\treturn strcmp(a, b);\n+}\n+\n+static int print_fields(int argc, const char **argv, struct repository *repo)\n+{\n+\tint ret = 0;\n+\tconst char *last = \"\";\n+\tstruct strbuf valbuf = STRBUF_INIT;\n+\tstruct strbuf quotbuf = STRBUF_INIT;\n+\n+\tQSORT(argv, argc, qsort_strcmp);\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tget_value_fn *get_value;\n+\t\tconst char *key = argv[i];\n+\n+\t\tstrbuf_reset(&valbuf);\n+\t\tstrbuf_reset(&quotbuf);\n+\n+\t\tif (!strcmp(key, last))\n+\t\t\tcontinue;\n+\n+\t\tlast = key;\n+\t\tget_value = get_value_fn_for_key(key);\n+\n+\t\tif (!get_value) {\n+\t\t\tret = error(_(\"key '%s' not found\"), key);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tget_value(repo, &valbuf);\n+\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n+\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\t}\n+\n+\tstrbuf_release(&valbuf);\n+\tstrbuf_release(&quotbuf);\n+\treturn ret;\n+}\n+\n+static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+\t\t     struct repository *repo)\n+{\n+\treturn print_fields(argc - 1, argv + 1, repo);\n+}\n+\n int cmd_repo(int argc, const char **argv, const char *prefix,\n \t     struct repository *repo)\n {\ndiff --git a/t/meson.build b/t/meson.build\nindex bbeba1a8d5..252dbbc031 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -230,6 +230,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nnew file mode 100755\nindex 0000000000..2b2516dbae\n--- /dev/null\n+++ b/t/t1900-repo.sh\n@@ -0,0 +1,57 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+\n+. ./test-lib.sh\n+\n+# Test whether a key-value pair is correctly returned\n+#\n+# Usage: test_repo_info <label> <init command> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init_command: a command which creates a repository\n+#   repo_name: the name of the repository that will be created in init_command\n+#   key: the key of the field that is being tested\n+#   expected_value: the value that the field should contain\n+test_repo_info () {\n+\tlabel=$1\n+\tinit_command=$2\n+\trepo_name=$3\n+\tkey=$4\n+\texpected_value=$5\n+\n+\ttest_expect_success \"$label\" '\n+\t\teval \"$init_command $repo_name\" &&\n+\t\techo \"$key=$expected_value\" >expected &&\n+\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n+\n+test_repo_info 'ref format files is retrieved correctly' '\n+\tgit init --ref-format=files' 'format-files' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' '\n+\tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n+\n+test_expect_success 'git-repo-info fails if an invalid key is requested' '\n+\techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n+\ttest_must_fail git repo info foo 2>actual_err &&\n+\ttest_cmp expected_err actual_err\n+'\n+\n+test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n+\techo \"references.format=$(test_detect_ref_format)\" >expected &&\n+\ttest_must_fail git repo info foo references.format bar >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'only one value is returned if the same key is requested twice' '\n+\tval=$(git rev-parse --show-ref-format) &&\n+\techo \"references.format=$val\" >expect &&\n+\tgit repo info references.format references.format >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523754","messageId":"20250807150239.6987-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v9 3/5] repo: add the field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-07T15:02:37Z","receivedAt":"2025-08-07T15:04:11Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag --is-bare-repository from git-rev-parse is used for retrieving\nwhether the current repository is bare. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field layout.bare to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              | 10 ++++++++++\n t/t1900-repo.sh             | 16 ++++++++++++++++\n 3 files changed, 29 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 0ee783abc2..0ef851ee9c 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -37,6 +37,9 @@ In order to obtain a set of values from `git repo info`, you should provide\n the keys that identify them. Here's a list of the available keys and the\n values that they return:\n \n+`layout.bare`::\n+`true` if this is a bare repository, otherwise `false`.\n+\n `references.format`::\n The reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 2b7ab5875e..abdc929e19 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,4 +1,7 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n+#include \"environment.h\"\n #include \"parse-options.h\"\n #include \"quote.h\"\n #include \"refs.h\"\n@@ -16,6 +19,12 @@ struct field {\n \tget_value_fn *get_value;\n };\n \n+static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf, is_bare_repository() ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -25,6 +34,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n \n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n+\t{ \"layout.bare\", get_layout_bare },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 2b2516dbae..e5a624ec71 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -35,6 +35,12 @@ test_repo_info 'ref format files is retrieved correctly' '\n test_repo_info 'ref format reftable is retrieved correctly' '\n \tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' \\\n+\t'git init' 'nonbare' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' \\\n+\t'git init --bare' 'bare' 'layout.bare' 'true'\n+\n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n \ttest_must_fail git repo info foo 2>actual_err &&\n@@ -54,4 +60,14 @@ test_expect_success 'only one value is returned if the same key is requested twi\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'output is returned correctly when two keys are requested' '\n+\tcat >expected <<-\\EOF &&\n+\tlayout.bare=false\n+\treferences.format=files\n+\tEOF\n+\tgit init --ref-format=files two-keys &&\n+\tgit -C two-keys repo info layout.bare references.format >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523755","messageId":"20250807150239.6987-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v9 4/5] repo: add the field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-07T15:02:38Z","receivedAt":"2025-08-07T15:04:15Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--is-shallow-repository` from git-rev-parse is used for\nretrieving whether the repository is shallow. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field `layout.shallow` to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              |  9 +++++++++\n t/t1900-repo.sh             | 10 ++++++++++\n 3 files changed, 22 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 0ef851ee9c..1ae9c09fac 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -40,6 +40,9 @@ values that they return:\n `layout.bare`::\n `true` if this is a bare repository, otherwise `false`.\n \n+`layout.shallow`::\n+`true` if this is a shallow repository, otherwise `false`.\n+\n `references.format`::\n The reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex abdc929e19..312fd08c34 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -6,6 +6,7 @@\n #include \"quote.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n+#include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n@@ -25,6 +26,13 @@ static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_layout_shallow(struct repository *repo, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf,\n+\t\t      is_repository_shallow(repo) ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -35,6 +43,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n /* repo_info_fields keys should be in lexicographical order */\n static const struct field repo_info_fields[] = {\n \t{ \"layout.bare\", get_layout_bare },\n+\t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex e5a624ec71..2018772631 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -41,6 +41,16 @@ test_repo_info 'bare repository = false is retrieved correctly' \\\n test_repo_info 'bare repository = true is retrieved correctly' \\\n \t'git init --bare' 'bare' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' \\\n+\t'git init' 'nonshallow' 'layout.shallow' 'false'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' \\\n+\t'git init remote &&\n+\techo x >remote/x &&\n+\tgit -C remote add x &&\n+\tgit -C remote commit -m x &&\n+\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n+\n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n \ttest_must_fail git repo info foo 2>actual_err &&\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523756","messageId":"20250807150239.6987-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v9 5/5] repo: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-07T15:02:39Z","receivedAt":"2025-08-07T15:04:19Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the --format flag to git-repo-info. By using this flag, the users\ncan choose the format for obtaining the data they requested.\n\nGiven that this command can be used for generating input for other\napplications and for being read by end users, it requires at least two\nformats: one for being read by humans and other for being read by\nmachines. Some other Git commands also have two output formats, notably\ngit-config which was the inspiration for the two formats that were\nchosen here:\n\n- keyvalue, where the retrieved data is printed one per line, using =\n  for delimiting the key and the value. This is the default format,\n  targeted for end users.\n- nul, where the retrieved data is separated by null characters, using\n  the newline character for delimiting the key and the value. This\n  format is targeted for being read by machines.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 36 ++++++++++++++++++++++++-----\n builtin/repo.c              | 46 ++++++++++++++++++++++++++++++++-----\n t/t1900-repo.sh             | 35 +++++++++++++++++++++-------\n 3 files changed, 97 insertions(+), 20 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 1ae9c09fac..2b63954098 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -8,7 +8,7 @@ git-repo - Retrieve information about the repository\n SYNOPSIS\n --------\n [synopsis]\n-git repo info [<key>...]\n+git repo info [--format=(keyvalue|nul)] [<key>...]\n \n DESCRIPTION\n -----------\n@@ -18,21 +18,28 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n \n COMMANDS\n --------\n-`info [<key>...]`::\n+`info [--format=(keyvalue|nul)] [<key>...]`::\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n +\n The returned data is lexicographically sorted by the keys.\n +\n-The output format consists of key-value pairs one per line using the `=`\n-character as the delimiter between the key and the value. Values containing\n-\"unusual\" characters are quoted as explained for the configuration variable\n+The output format can be chosen through the flag `--format`. Two formats are\n+supported:\n++\n+* `keyvalue`: output key-value pairs one per line using the `=` character as\n+the delimiter between the key and the value. Values containing \"unusual\"\n+characters are quoted as explained for the configuration variable\n `core.quotePath` (see linkgit:git-config[1]). This is the default.\n \n+* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n+between the key and the value and using a null character after each value.\n+This format is better suited for being parsed by another applications than\n+`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n+\n INFO KEYS\n ---------\n-\n In order to obtain a set of values from `git repo info`, you should provide\n the keys that identify them. Here's a list of the available keys and the\n values that they return:\n@@ -48,6 +55,23 @@ The reference storage format. The valid values are:\n +\n include::ref-storage-format.adoc[]\n \n+EXAMPLES\n+--------\n+\n+* Retrieves the reference format of the current repository:\n++\n+------------\n+git repo info references.format\n+------------\n++\n+\n+* Retrieves whether the current repository is bare and whether it is shallow\n+using the `nul` format:\n++\n+------------\n+git repo info --format=nul layout.bare layout.shallow\n+------------\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 312fd08c34..aca29729eb 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -9,12 +9,17 @@\n #include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n-\t\"git repo info [<key>...]\",\n+\t\"git repo info [--format=(keyvalue|nul)] [<key>...]\",\n \tNULL\n };\n \n typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n \n+enum output_format {\n+\tFORMAT_KEYVALUE,\n+\tFORMAT_NUL_TERMINATED,\n+};\n+\n struct field {\n \tconst char *key;\n \tget_value_fn *get_value;\n@@ -73,7 +78,9 @@ static int qsort_strcmp(const void *va, const void *vb)\n \treturn strcmp(a, b);\n }\n \n-static int print_fields(int argc, const char **argv, struct repository *repo)\n+static int print_fields(int argc, const char **argv,\n+\t\t\tstruct repository *repo,\n+\t\t\tenum output_format format)\n {\n \tint ret = 0;\n \tconst char *last = \"\";\n@@ -101,8 +108,18 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \t\t}\n \n \t\tget_value(repo, &valbuf);\n-\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n-\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\n+\t\tswitch (format) {\n+\t\tcase FORMAT_KEYVALUE:\n+\t\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n+\t\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\t\t\tbreak;\n+\t\tcase FORMAT_NUL_TERMINATED:\n+\t\t\tprintf(\"%s\\n%s%c\", key, valbuf.buf, '\\0');\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"not a valid output format: %d\", format);\n+\t\t}\n \t}\n \n \tstrbuf_release(&valbuf);\n@@ -110,10 +127,27 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \treturn ret;\n }\n \n-static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+static int repo_info(int argc, const char **argv, const char *prefix,\n \t\t     struct repository *repo)\n {\n-\treturn print_fields(argc - 1, argv + 1, repo);\n+\tconst char *format_str = \"keyvalue\";\n+\tenum output_format format;\n+\tstruct option options[] = {\n+\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n+\t\t\t   N_(\"output format\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\tif (!strcmp(format_str, \"keyvalue\"))\n+\t\tformat = FORMAT_KEYVALUE;\n+\telse if (!strcmp(format_str, \"nul\"))\n+\t\tformat = FORMAT_NUL_TERMINATED;\n+\telse\n+\t\tdie(_(\"invalid format '%s'\"), format_str);\n+\n+\treturn print_fields(argc, argv, repo, format);\n }\n \n int cmd_repo(int argc, const char **argv, const char *prefix,\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 2018772631..eb37c9d16b 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -21,12 +21,22 @@ test_repo_info () {\n \tkey=$4\n \texpected_value=$5\n \n-\ttest_expect_success \"$label\" '\n-\t\teval \"$init_command $repo_name\" &&\n-\t\techo \"$key=$expected_value\" >expected &&\n-\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\trepo_name_keyvalue=\"$repo_name\"-keyvalue\n+\trepo_name_nul=\"$repo_name\"-nul\n+\n+\ttest_expect_success \"keyvalue: $label\" '\n+\t\teval \"$init_command $repo_name_keyvalue\" &&\n+\t\techo \"$key=$expected_value\" > expected &&\n+\t\tgit -C \"$repo_name_keyvalue\" repo info \"$key\" >actual &&\n \t\ttest_cmp expected actual\n \t'\n+\n+\ttest_expect_success \"nul: $label\" '\n+\t\teval \"$init_command $repo_name_nul\" &&\n+\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n+\t\tgit -C \"$repo_name_nul\" repo info --format=nul \"$key\" >actual &&\n+\t\ttest_cmp_bin expected actual\n+\t'\n }\n \n test_repo_info 'ref format files is retrieved correctly' '\n@@ -44,12 +54,15 @@ test_repo_info 'bare repository = true is retrieved correctly' \\\n test_repo_info 'shallow repository = false is retrieved correctly' \\\n \t'git init' 'nonshallow' 'layout.shallow' 'false'\n \n-test_repo_info 'shallow repository = true is retrieved correctly' \\\n-\t'git init remote &&\n+test_expect_success 'setup remote' '\n+\tgit init remote &&\n \techo x >remote/x &&\n \tgit -C remote add x &&\n-\tgit -C remote commit -m x &&\n-\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n+\tgit -C remote commit -m x\n+'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' \\\n+\t'git clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n \n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n@@ -80,4 +93,10 @@ test_expect_success 'output is returned correctly when two keys are requested' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n+\techo \"fatal: invalid format '\\'foo\\''\" >expected &&\n+\ttest_must_fail git repo info --format=foo 2>err &&\n+\ttest_cmp expected err\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"523759","messageId":"xmqqjz3frv9i.fsf@gitster.g","threadId":"63618","inReplyTo":"aJQ3tyC0e53iWtVE@pks.im","subject":"Re: [GSoC PATCH v8 5/5] repo: add the --format flag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-07T15:44:57Z","receivedAt":"2025-08-07T15:45:00Z","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>> +\t\tdefault:\n>> +\t\t\tBUG(\"%d: not a valid output format\", format);\n>\n> Nit: we typically say it the other way round.\n>\n> \tBUG(\"not a valid output format: %d\", format);\n\nIt is in the coding guidelines\n\n - Say what the error is first (\"cannot open '%s'\", not \"%s: cannot open\").\n\nbut sometimes we forget.  Thanks for good eyes.\n\n"},{"id":"523799","messageId":"aJWO78GrsUc7iBFt@pks.im","threadId":"63618","inReplyTo":"20250807150239.6987-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v9 0/5] repo: add new command for retrieving repository info","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-08-08T05:45:19Z","receivedAt":"2025-08-08T05:45:27Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Aug 07, 2025 at 12:02:34PM -0300, Lucas Seiki Oshiro wrote:\n> Hi!\n> \n> Thank you all for your time reviewing and helping me with this patchset!\n> \n> This v9 only solve tiny nitpicks pointed by Karthik and Patrick in v8,\n> just to make clear that everything is ok!\n> \n> Junio, would you mind to replace v8 by this v9 as lo/repo-info in seen?\n\nThanks, this version looks good to me!\n\nPatrick\n"},{"id":"523811","messageId":"CAOLa=ZTDFszwbQc=U-WEvmquymT_gmZq84-5QJ5VgtyHvXDtaw@mail.gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v9 0/5] repo: add new command for retrieving repository info","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-08-08T09:20:36Z","receivedAt":"2025-08-08T09:20:38Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Hi!\n>\n> Thank you all for your time reviewing and helping me with this patchset!\n>\n> This v9 only solve tiny nitpicks pointed by Karthik and Patrick in v8,\n> just to make clear that everything is ok!\n>\n> Junio, would you mind to replace v8 by this v9 as lo/repo-info in seen?\n>\n\nThe changes look good, Thanks.\n\n[snip]\n"},{"id":"523821","messageId":"xmqqh5yhkgab.fsf@gitster.g","threadId":"63618","inReplyTo":"aJWO78GrsUc7iBFt@pks.im","subject":"Re: [GSoC PATCH v9 0/5] repo: add new command for retrieving repository info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-08T15:02:36Z","receivedAt":"2025-08-08T15:02:40Z","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 Thu, Aug 07, 2025 at 12:02:34PM -0300, Lucas Seiki Oshiro wrote:\n>> Hi!\n>> \n>> Thank you all for your time reviewing and helping me with this patchset!\n>> \n>> This v9 only solve tiny nitpicks pointed by Karthik and Patrick in v8,\n>> just to make clear that everything is ok!\n>> \n>> Junio, would you mind to replace v8 by this v9 as lo/repo-info in seen?\n>\n> Thanks, this version looks good to me!\n>\n> Patrick\n\nThanks, both.  Let's mark it for 'next' now.\n\n5753e542 (repo: add the --format flag, 2025-08-07)\n"},{"id":"523904","messageId":"CAPig+cTA=UKbHd+BdCQUdTjvz1AzCNHi2XTREPhfUJfz_2Ny_A@mail.gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v9 2/5] repo: add the field references.format","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-11T05:12:51Z","receivedAt":"2025-08-11T05:13:04Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Aug 7, 2025 at 11:04 AM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> This commit is part of the series that introduces the new subcommand\n> git-repo-info.\n>\n> The flag `--show-ref-format` from git-rev-parse is used for retrieving\n> the reference format (i.e. `files` or `reftable`). This way, it is\n> used for querying repository metadata, fitting in the purpose of\n> git-repo-info.\n>\n> Add a new field `references.format` to the repo-info subcommand\n> containing that information.\n>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\n> @@ -22,6 +22,25 @@ COMMANDS\n>         Retrieve metadata-related information about the current repository. Only\n>         the requested data will be returned based on their keys (see \"INFO KEYS\"\n>         section below).\n> ++\n> +The returned data is lexicographically sorted by the keys.\n> ++\n> +The output format consists of key-value pairs one per line using the `=`\n> +character as the delimiter between the key and the value. Values containing\n> +\"unusual\" characters are quoted as explained for the configuration variable\n> +`core.quotePath` (see linkgit:git-config[1]). This is the default.\n\nI don't see any alternative formats presented, so what does \"This is\nthe default\" mean here?\n\n(I'm guessing that it might gain meaning in a later patch when NUL\noutput format is added, but lacking such context in this patch, the\nsentence is more than a bit confusing.)\n\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> @@ -1,17 +1,102 @@\n> +/* repo_info_fields keys should be in lexicographical order */\n> +static const struct field repo_info_fields[] = {\n> +       { \"references.format\", get_references_format },\n> +};\n\nThe comment ought to be more assertive: s/should/must/\n\n> +static int print_fields(int argc, const char **argv, struct repository *repo)\n> +{\n> +       struct strbuf valbuf = STRBUF_INIT;\n> +       struct strbuf quotbuf = STRBUF_INIT;\n> +\n> +       for (int i = 0; i < argc; i++) {\n> +               get_value_fn *get_value;\n> +               const char *key = argv[i];\n> +\n> +               strbuf_reset(&valbuf);\n> +               strbuf_reset(&quotbuf);\n> +\n> +               if (!strcmp(key, last))\n> +                       continue;\n> +\n> +               last = key;\n> +               get_value = get_value_fn_for_key(key);\n> +\n> +               if (!get_value) {\n> +                       ret = error(_(\"key '%s' not found\"), key);\n> +                       continue;\n> +               }\n> +\n> +               get_value(repo, &valbuf);\n> +               quote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n> +               printf(\"%s=%s\\n\", key, quotbuf.buf);\n> +       }\n\nNit: To avoid unnecessary work in the two `continue` cases, I would\nhave placed the strbuf_reset() calls just before the call to\nget_value() as illustrated in my earlier review[1]. Subjective and not\nworth a reroll, though.\n\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> @@ -0,0 +1,57 @@\n> +# Test whether a key-value pair is correctly returned\n> +#\n> +# Usage: test_repo_info <label> <init command> <key> <expected value>\n> +#\n> +# Arguments:\n> +#   label: the label of the test\n> +#   init_command: a command which creates a repository\n> +#   repo_name: the name of the repository that will be created in init_command\n> +#   key: the key of the field that is being tested\n> +#   expected_value: the value that the field should contain\n\nThe \"Usage\" is still wrong (as mentioned earlier[1]). It shows only\nfour arguments despite the function taking five.\n\n> +test_repo_info () {\n> +       label=$1\n> +       init_command=$2\n> +       repo_name=$3\n> +       key=$4\n> +       expected_value=$5\n> +\n> +       test_expect_success \"$label\" '\n> +               eval \"$init_command $repo_name\" &&\n> +               echo \"$key=$expected_value\" >expected &&\n> +               git -C $repo_name repo info \"$key\" >actual &&\n> +               test_cmp expected actual\n> +       '\n> +}\n> +\n> +test_repo_info 'ref format files is retrieved correctly' '\n> +       git init --ref-format=files' 'format-files' 'references.format' 'files'\n> +\n> +test_repo_info 'ref format reftable is retrieved correctly' '\n> +       git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n\nThe quote placement used in these calls to `test_repo_info` is still\nunusual and confusing, as mentioned previously[2]. Calling the\nfunction in the more traditional way would be preferable:\n\n    test_repo_info 'ref format files is retrieved correctly' \\\n        'git init --ref-format=files' 'format-files' 'references.format' 'files'\n\n> +test_expect_success 'git-repo-info fails if an invalid key is requested' '\n> +       echo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n> +       test_must_fail git repo info foo 2>actual_err &&\n> +       test_cmp expected_err actual_err\n> +'\n> +\n> +test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n> +       echo \"references.format=$(test_detect_ref_format)\" >expected &&\n> +       test_must_fail git repo info foo references.format bar >actual &&\n> +       test_cmp expected actual\n> +'\n> +\n> +test_expect_success 'only one value is returned if the same key is requested twice' '\n> +       val=$(git rev-parse --show-ref-format) &&\n> +       echo \"references.format=$val\" >expect &&\n> +       git repo info references.format references.format >actual &&\n> +       test_cmp expect actual\n> +'\n\nIn my previous review[1], I identified a problem in which the logic\nwould/could present a poor user-experience by emitting \"key '%s' not\nfound\" multiple times for a given unknown key, but I don't see a test\nverifying that this problem has been fixed.\n\n[1]: https://lore.kernel.org/git/CAPig+cTxNUPayO2SdCL-BPtjb2rfr3e3RK=BsQxAiiEAtpBaRg@mail.gmail.com/\n[2]: https://lore.kernel.org/git/CAPig+cR=vRu7GwGx_wpS_GZNdX7giosDK12K+qQdOW1va-6oWw@mail.gmail.com/\n"},{"id":"523905","messageId":"CAPig+cSBg6oQC4Y81ieH25-A9cHRTfbqC5i+22RSBcbyVo_qtA@mail.gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-4-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v9 3/5] repo: add the field layout.bare","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-11T05:21:54Z","receivedAt":"2025-08-11T05:22:06Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Aug 7, 2025 at 11:04 AM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> This commit is part of the series that introduces the new subcommand\n> git-repo-info.\n>\n> The flag --is-bare-repository from git-rev-parse is used for retrieving\n> whether the current repository is bare. This way, it is used for\n> querying repository metadata, fitting in the purpose of git-repo-info.\n>\n> Then, add a new field layout.bare to the git-repo-info subcommand\n> containing that information.\n>\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> @@ -54,4 +60,14 @@ test_expect_success 'only one value is returned if the same key is requested twi\n> +test_expect_success 'output is returned correctly when two keys are requested' '\n> +       cat >expected <<-\\EOF &&\n> +       layout.bare=false\n> +       references.format=files\n> +       EOF\n> +       git init --ref-format=files two-keys &&\n> +       git -C two-keys repo info layout.bare references.format >actual &&\n> +       test_cmp expected actual\n> +'\n\nSince the documentation asserts that the emitted key/value lines will\nbe sorted lexicographically, can we also have a test that verifies\nthat behavior? There are a couple ways you could do that: (1) either\nadd another test just like this one but reverse the order of the\narguments to the `git repo info` invocation, or (2) modify this test\nby reversing the arguments and (*importantly*) add a comment to the\ntest body explaining that the order of the arguments to the command\ninvocation are intentionally different from the output to prove that\nthe output order is unrelated to the argument order.\n"},{"id":"523906","messageId":"CAPig+cS0F0KYrgfspGGrYtGuisWxDqC+VgKuASRghBE4aFSiTg@mail.gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v9 5/5] repo: add the --format flag","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-11T05:44:23Z","receivedAt":"2025-08-11T05:44:36Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Aug 7, 2025 at 11:04 AM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> Add the --format flag to git-repo-info. By using this flag, the users\n> can choose the format for obtaining the data they requested.\n> [...]\n> Signed-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n> ---\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> @@ -21,12 +21,22 @@ test_repo_info () {\n> -       test_expect_success \"$label\" '\n> -               eval \"$init_command $repo_name\" &&\n> -               echo \"$key=$expected_value\" >expected &&\n> -               git -C $repo_name repo info \"$key\" >actual &&\n> +       repo_name_keyvalue=\"$repo_name\"-keyvalue\n> +       repo_name_nul=\"$repo_name\"-nul\n> +\n> +       test_expect_success \"keyvalue: $label\" '\n> +               eval \"$init_command $repo_name_keyvalue\" &&\n> +               echo \"$key=$expected_value\" > expected &&\n\nStyle nit: drop space following redirection operator[1]. Not worth a reroll.\n\n> +               git -C \"$repo_name_keyvalue\" repo info \"$key\" >actual &&\n>                 test_cmp expected actual\n>         '\n> +\n> +       test_expect_success \"nul: $label\" '\n> +               eval \"$init_command $repo_name_nul\" &&\n> +               printf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n> +               git -C \"$repo_name_nul\" repo info --format=nul \"$key\" >actual &&\n> +               test_cmp_bin expected actual\n> +       '\n>  }\n> @@ -44,12 +54,15 @@ test_repo_info 'bare repository = true is retrieved correctly' \\\n>  test_expect_success 'git-repo-info fails if an invalid key is requested' '\n>         echo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n\nNit: Here you used ${SQ} as suggested by Karthik[2]...\n\n> @@ -80,4 +93,10 @@ test_expect_success 'output is returned correctly when two keys are requested' '\n> +test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n> +       echo \"fatal: invalid format '\\'foo\\''\" >expected &&\n\n...but here you did not.\n\n> +       test_must_fail git repo info --format=foo 2>err &&\n> +       test_cmp expected err\n> +'\n\n[1]: https://lore.kernel.org/git/aJQ3sVf4MsgnCaMz@pks.im/\n[2]: https://lore.kernel.org/git/CAOLa=ZSX0hFt7PRdXssz2xGG17bmDchS=EheBSmQj9xr+r_baA@mail.gmail.com/\n"},{"id":"523947","messageId":"f97b712f-95a4-480b-8ed0-174e4f45ff81@gmail.com","threadId":"63618","inReplyTo":"20250807150239.6987-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v9 2/5] repo: add the field references.format","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-08-11T14:41:30Z","receivedAt":"2025-08-11T14:41:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Lucas\n\nOn 07/08/2025 16:02, Lucas Seiki Oshiro wrote:\n> ++\n> +The returned data is lexicographically sorted by the keys.\n\nWhat's the reason for this? If I query three keys from a script then it \nis much easier to parse the output if I know the keys are going to \nappear in the same order that they were on the command line. If the \ncommand re-orders them my script now has to check the value of each key \nwhich results in a bunch of unnecessary string comparisons because it \ncannot determine the key from the position in the output. While we were \nproducing json output there was a need to de-duplicate the keys when \nthat output format was selected. However, we no-longer produce json and \nin any case de-duplication could have been achieved without sorting the \ninput keys by using a hash table, or, as there is a small fixed number \nof keys, an array that records the keys we've already seen.\n\nThanks\n\nPhillip\n\n> ++\n> +The output format consists of key-value pairs one per line using the `=`\n> +character as the delimiter between the key and the value. Values containing\n> +\"unusual\" characters are quoted as explained for the configuration variable\n> +`core.quotePath` (see linkgit:git-config[1]). This is the default.\n> +\n> +INFO KEYS\n> +---------\n> +\n> +In order to obtain a set of values from `git repo info`, you should provide\n> +the keys that identify them. Here's a list of the available keys and the\n> +values that they return:\n> +\n> +`references.format`::\n> +The reference storage format. The valid values are:\n> ++\n> +include::ref-storage-format.adoc[]\n>   \n>   SEE ALSO\n>   --------\n> diff --git a/builtin/repo.c b/builtin/repo.c\n> index fd2a9b4216..2b7ab5875e 100644\n> --- a/builtin/repo.c\n> +++ b/builtin/repo.c\n> @@ -1,17 +1,102 @@\n>   #include \"builtin.h\"\n>   #include \"parse-options.h\"\n> +#include \"quote.h\"\n> +#include \"refs.h\"\n> +#include \"strbuf.h\"\n>   \n>   static const char *const repo_usage[] = {\n>   \t\"git repo info [<key>...]\",\n>   \tNULL\n>   };\n>   \n> -static int repo_info(int argc UNUSED, const char **argv UNUSED,\n> -\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n> +typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n> +\n> +struct field {\n> +\tconst char *key;\n> +\tget_value_fn *get_value;\n> +};\n> +\n> +static int get_references_format(struct repository *repo, struct strbuf *buf)\n>   {\n> +\tstrbuf_addstr(buf,\n> +\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n>   \treturn 0;\n>   }\n>   \n> +/* repo_info_fields keys should be in lexicographical order */\n> +static const struct field repo_info_fields[] = {\n> +\t{ \"references.format\", get_references_format },\n> +};\n> +\n> +static int repo_info_fields_cmp(const void *va, const void *vb)\n> +{\n> +\tconst struct field *a = va;\n> +\tconst struct field *b = vb;\n> +\n> +\treturn strcmp(a->key, b->key);\n> +}\n> +\n> +static get_value_fn *get_value_fn_for_key(const char *key)\n> +{\n> +\tconst struct field search_key = { key, NULL };\n> +\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n> +\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n> +\t\t\t\t\t    sizeof(*found),\n> +\t\t\t\t\t    repo_info_fields_cmp);\n> +\treturn found ? found->get_value : NULL;\n> +}\n> +\n> +static int qsort_strcmp(const void *va, const void *vb)\n> +{\n> +\tconst char *a = *(const char **)va;\n> +\tconst char *b = *(const char **)vb;\n> +\n> +\treturn strcmp(a, b);\n> +}\n> +\n> +static int print_fields(int argc, const char **argv, struct repository *repo)\n> +{\n> +\tint ret = 0;\n> +\tconst char *last = \"\";\n> +\tstruct strbuf valbuf = STRBUF_INIT;\n> +\tstruct strbuf quotbuf = STRBUF_INIT;\n> +\n> +\tQSORT(argv, argc, qsort_strcmp);\n> +\n> +\tfor (int i = 0; i < argc; i++) {\n> +\t\tget_value_fn *get_value;\n> +\t\tconst char *key = argv[i];\n> +\n> +\t\tstrbuf_reset(&valbuf);\n> +\t\tstrbuf_reset(&quotbuf);\n> +\n> +\t\tif (!strcmp(key, last))\n> +\t\t\tcontinue;\n> +\n> +\t\tlast = key;\n> +\t\tget_value = get_value_fn_for_key(key);\n> +\n> +\t\tif (!get_value) {\n> +\t\t\tret = error(_(\"key '%s' not found\"), key);\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tget_value(repo, &valbuf);\n> +\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n> +\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n> +\t}\n> +\n> +\tstrbuf_release(&valbuf);\n> +\tstrbuf_release(&quotbuf);\n> +\treturn ret;\n> +}\n> +\n> +static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n> +\t\t     struct repository *repo)\n> +{\n> +\treturn print_fields(argc - 1, argv + 1, repo);\n> +}\n> +\n>   int cmd_repo(int argc, const char **argv, const char *prefix,\n>   \t     struct repository *repo)\n>   {\n> diff --git a/t/meson.build b/t/meson.build\n> index bbeba1a8d5..252dbbc031 100644\n> --- a/t/meson.build\n> +++ b/t/meson.build\n> @@ -230,6 +230,7 @@ integration_tests = [\n>     't1700-split-index.sh',\n>     't1701-racy-split-index.sh',\n>     't1800-hook.sh',\n> +  't1900-repo.sh',\n>     't2000-conflict-when-checking-files-out.sh',\n>     't2002-checkout-cache-u.sh',\n>     't2003-checkout-cache-mkdir.sh',\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> new file mode 100755\n> index 0000000000..2b2516dbae\n> --- /dev/null\n> +++ b/t/t1900-repo.sh\n> @@ -0,0 +1,57 @@\n> +#!/bin/sh\n> +\n> +test_description='test git repo-info'\n> +\n> +. ./test-lib.sh\n> +\n> +# Test whether a key-value pair is correctly returned\n> +#\n> +# Usage: test_repo_info <label> <init command> <key> <expected value>\n> +#\n> +# Arguments:\n> +#   label: the label of the test\n> +#   init_command: a command which creates a repository\n> +#   repo_name: the name of the repository that will be created in init_command\n> +#   key: the key of the field that is being tested\n> +#   expected_value: the value that the field should contain\n> +test_repo_info () {\n> +\tlabel=$1\n> +\tinit_command=$2\n> +\trepo_name=$3\n> +\tkey=$4\n> +\texpected_value=$5\n> +\n> +\ttest_expect_success \"$label\" '\n> +\t\teval \"$init_command $repo_name\" &&\n> +\t\techo \"$key=$expected_value\" >expected &&\n> +\t\tgit -C $repo_name repo info \"$key\" >actual &&\n> +\t\ttest_cmp expected actual\n> +\t'\n> +}\n> +\n> +test_repo_info 'ref format files is retrieved correctly' '\n> +\tgit init --ref-format=files' 'format-files' 'references.format' 'files'\n> +\n> +test_repo_info 'ref format reftable is retrieved correctly' '\n> +\tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n> +\n> +test_expect_success 'git-repo-info fails if an invalid key is requested' '\n> +\techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n> +\ttest_must_fail git repo info foo 2>actual_err &&\n> +\ttest_cmp expected_err actual_err\n> +'\n> +\n> +test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n> +\techo \"references.format=$(test_detect_ref_format)\" >expected &&\n> +\ttest_must_fail git repo info foo references.format bar >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'only one value is returned if the same key is requested twice' '\n> +\tval=$(git rev-parse --show-ref-format) &&\n> +\techo \"references.format=$val\" >expect &&\n> +\tgit repo info references.format references.format >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_done\n\n"},{"id":"523953","messageId":"xmqq7bz950dd.fsf@gitster.g","threadId":"63618","inReplyTo":"f97b712f-95a4-480b-8ed0-174e4f45ff81@gmail.com","subject":"Re: [GSoC PATCH v9 2/5] repo: add the field references.format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-11T15:44:30Z","receivedAt":"2025-08-11T15:44:33Z","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> Hi Lucas\n>\n> On 07/08/2025 16:02, Lucas Seiki Oshiro wrote:\n>> ++\n>> +The returned data is lexicographically sorted by the keys.\n>\n> What's the reason for this? If I query three keys from a script then\n> it is much easier to parse the output if I know the keys are going to\n> appear in the same order that they were on the command line. If the\n> command re-orders them my script now has to check the value of each\n> key which results in a bunch of unnecessary string comparisons because\n> it cannot determine the key from the position in the output. While we\n> were producing json output there was a need to de-duplicate the keys\n> when that output format was selected. However, we no-longer produce\n> json and in any case de-duplication could have been achieved without\n> sorting the input keys by using a hash table, or, as there is a small\n> fixed number of keys, an array that records the keys we've already\n> seen.\n\nVery good.  Thanks.\n"},{"id":"524153","messageId":"8A30D2A7-3290-4E7C-BBF5-A51AE5AD19F0@gmail.com","threadId":"63618","inReplyTo":"f97b712f-95a4-480b-8ed0-174e4f45ff81@gmail.com","subject":"Re: [GSoC PATCH v9 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-13T21:18:04Z","receivedAt":"2025-08-13T21:18:20Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> What's the reason for this?\n\nBasically, filter out duplicated keys. This is also helpful for not \nrepeating the same \"key not found\" multiple times, as suggested by \nEric [1].\n\nI could also use other data structures for doing that, but I think it\nwould make the code too complex without having a real benefit.\n\n> If I query three keys from a script then it is much easier to parse\n> the output if I know the keys are going to appear in the same order\n> that they were on the command line.\n\nThis assumption would be a little bit broken as one can ask an invalid\nkey. In this case, this command will print the error to stderr, and\nproceed to the next value.\n\n> If the command re-orders them my script now has to check the value of\n> each key which results in a bunch of unnecessary string comparisons\n> because it cannot determine the key from the position in the output.\n\nIn cases where the client don't want to compare strings, it is still\npossible to ask one key at time, just like other Git commands (e.g.\ngit var, git config). Since this command won't return too many values,\nit would be ok even if the user requests all the possible keys.\n\n> While we were producing json output there was a need to de-duplicate\n> the keys when that output format was selected. However, we no-longer\n> produce json and in any case de-duplication could have been achieved\n> without sorting the input keys by using a hash table, or, as there is\n> a small fixed number of keys, an array that records the keys we've\n> already seen.\n\nI still think that it would over-engineer this command. If I follow\nthis path of returning the values in the same order they were in the\ncommand line, I think it would be better to just allow duplicated keys\nand multiple \"key not found\" errors for the same unknown key instead\nof increasing the complexity of this command.\n\nWhat do you think?\n\n[1] https://lore.kernel.org/git/CAPig+cTxNUPayO2SdCL-BPtjb2rfr3e3RK=BsQxAiiEAtpBaRg@mail.gmail.com/"},{"id":"524155","messageId":"CAPig+cQUuXxz2gMMA200D39dqWi6wpu6z43Wkr_trbCwuvTKSA@mail.gmail.com","threadId":"63618","inReplyTo":"8A30D2A7-3290-4E7C-BBF5-A51AE5AD19F0@gmail.com","subject":"Re: [GSoC PATCH v9 2/5] repo: add the field references.format","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-13T21:46:15Z","receivedAt":"2025-08-13T21:46:30Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Aug 13, 2025 at 5:18 PM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> > What's the reason for this?\n>\n> Basically, filter out duplicated keys. This is also helpful for not\n> repeating the same \"key not found\" multiple times, as suggested by\n> Eric [1].\n\nThe suggestion you cite has relevance only as long as deduplication is\nthe chosen implementation scheme, however, Phillip is arguing that the\ndeduplication and key reordering logic should be dropped, hence, the\ncited reference isn't relevant in light of Phillip's suggestion.\n\n> > If I query three keys from a script then it is much easier to parse\n> > the output if I know the keys are going to appear in the same order\n> > that they were on the command line.\n>\n> This assumption would be a little bit broken as one can ask an invalid\n> key. In this case, this command will print the error to stderr, and\n> proceed to the next value.\n\nYes and no. While it's true that a caller might ask for an invalid\nkey, the primary (and useful) purpose of this command is to facilitate\nscripting. Once the script author has \"debugged\" the call to `git\nrepo`, then the output will be predictable. Hence, although you make a\nfair point, it's not a strong argument against Phillip's\nrecommendation to drop the deduplication and key re-ordering logic.\n\n> > If the command re-orders them my script now has to check the value of\n> > each key which results in a bunch of unnecessary string comparisons\n> > because it cannot determine the key from the position in the output.\n>\n> In cases where the client don't want to compare strings, it is still\n> possible to ask one key at time, just like other Git commands (e.g.\n> git var, git config). Since this command won't return too many values,\n> it would be ok even if the user requests all the possible keys.\n\nGenerally speaking, process creation is slow. Process creation on\nMicrosoft Windows is especially slow, excruciatingly so. Authors of\ntooling around Git often pay close attention to such matters because\nthey don't want the functionality provided by their tooling to be\nslow, so we ought to be weary of a counterargument (such as the one\nabove) which suggests simply running the command multiple times, once\nfor each item.\n\n> > While we were producing json output there was a need to de-duplicate\n> > the keys when that output format was selected. However, we no-longer\n> > produce json and in any case de-duplication could have been achieved\n> > without sorting the input keys by using a hash table, or, as there is\n> > a small fixed number of keys, an array that records the keys we've\n> > already seen.\n>\n> I still think that it would over-engineer this command.\n\nI don't think that Phillip was suggesting dropping only the reordering\nwhile keeping the deduplication; he was merely giving an example of an\nalternative implementation which would accomplish the deduplication\ngoal, so he wasn't asking to over-engineer. Instead, (according to my\nreading), he is suggesting dropping both deduplication and reordering.\n\n> If I follow\n> this path of returning the values in the same order they were in the\n> command line, I think it would be better to just allow duplicated keys\n> and multiple \"key not found\" errors for the same unknown key instead\n> of increasing the complexity of this command.\n>\n> What do you think?\n\nI think that's exactly what Phillip was suggesting: present output in\norder requested, no deduplication\n\nI had suggested the same back in [*], but I also said that I could\nformulate arguments in favor of either behavior, so I didn't have a\nstrong opinion. However, Phillip has presented a good reason to prefer\n\"output in order requested, no deduplication\", and I do find his\nargument compelling.\n\n[*]: https://lore.kernel.org/git/CAPig+cTuiUy=+2Jf1Lrp1gaM03_zPf8EFMVSKmShqU05t-3aWQ@mail.gmail.com/\n"},{"id":"524157","messageId":"9A8834AC-0AC1-4AE8-B430-4DE12F9079F9@gmail.com","threadId":"63618","inReplyTo":"CAPig+cQUuXxz2gMMA200D39dqWi6wpu6z43Wkr_trbCwuvTKSA@mail.gmail.com","subject":"Re: [GSoC PATCH v9 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-13T22:24:37Z","receivedAt":"2025-08-13T22:24:53Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> The suggestion you cite has relevance only as long as deduplication is\n> the chosen implementation scheme, however, Phillip is arguing that the\n> deduplication and key reordering logic should be dropped, hence, the\n> cited reference isn't relevant in light of Phillip's suggestion.\n\nOk!\n\n> However, Phillip has presented a good reason to prefer\n> \"output in order requested, no deduplication\", and I do find his\n> argument compelling.\n\nI'll do that! Thanks!\n"},{"id":"524172","messageId":"6731afaf-7036-4c8b-9479-80b6e2afa7e7@gmail.com","threadId":"63618","inReplyTo":"CAPig+cQUuXxz2gMMA200D39dqWi6wpu6z43Wkr_trbCwuvTKSA@mail.gmail.com","subject":"Re: [GSoC PATCH v9 2/5] repo: add the field references.format","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-08-14T13:58:07Z","receivedAt":"2025-08-14T13:58:10Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 13/08/2025 22:46, Eric Sunshine wrote:\n> On Wed, Aug 13, 2025 at 5:18 PM Lucas Seiki Oshiro\n> <lucasseikioshiro@gmail.com> wrote:\n> \n> I don't think that Phillip was suggesting dropping only the reordering\n> while keeping the deduplication; he was merely giving an example of an\n> alternative implementation which would accomplish the deduplication\n> goal, so he wasn't asking to over-engineer. Instead, (according to my\n> reading), he is suggesting dropping both deduplication and reordering.\n\nExactly\n>> If I follow\n>> this path of returning the values in the same order they were in the\n>> command line, I think it would be better to just allow duplicated keys\n>> and multiple \"key not found\" errors for the same unknown key instead\n>> of increasing the complexity of this command.\n>>\n>> What do you think?\n> \n> I think that's exactly what Phillip was suggesting: present output in\n> order requested, no deduplication\n> \n> I had suggested the same back in [*], but I also said that I could\n> formulate arguments in favor of either behavior, so I didn't have a\n> strong opinion. However, Phillip has presented a good reason to prefer\n> \"output in order requested, no deduplication\", and I do find his\n> argument compelling.\n\nIf this command was producing output primarily for human consumption \nthen I think the argument for sorting and de-duplicating the output \nwould be much stronger. However, as this command is intended to replace \nsome uses of \"git rev-parse\" in scripts, I think that \"output in order \nrequested, no de-duplication\" is more convenient.\n\nThanks\n\nPhillip\n\n"},{"id":"524183","messageId":"BCCE25CF-8546-4320-AB44-9B729C3D6DE6@gmail.com","threadId":"63618","inReplyTo":"CAPig+cSBg6oQC4Y81ieH25-A9cHRTfbqC5i+22RSBcbyVo_qtA@mail.gmail.com","subject":"Re: [GSoC PATCH v9 3/5] repo: add the field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-14T18:22:44Z","receivedAt":"2025-08-14T18:23:00Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> Since the documentation asserts that the emitted key/value lines will\n> be sorted lexicographically, can we also have a test that verifies\n> that behavior?\n\nSince we agreed to return the values in the order they were requested,\nI'll test that order instead:\n\ntest_expect_success 'the values are returned in the same order they were requested' '\n\tprintf \"references.format=files\\nlayout.bare=false\\n\" >expected1 &&\n\tprintf \"layout.bare=false\\nreferences.format=files\\n\" >expected2 &&\n\tgit init --ref-format=files same-order &&\n\tgit -C same-order repo info references.format layout.bare >actual1 &&\n\tgit -C same-order repo info layout.bare references.format >actual2 &&\n\ttest_cmp expected1 actual1 &&\n\ttest_cmp expected2 actual2\n'\n\n"},{"id":"524184","messageId":"CAPig+cTQdEDZduG_UmdHn1JNfj7iTw0=jwdkX+VPJ11Jo0jUGA@mail.gmail.com","threadId":"63618","inReplyTo":"BCCE25CF-8546-4320-AB44-9B729C3D6DE6@gmail.com","subject":"Re: [GSoC PATCH v9 3/5] repo: add the field layout.bare","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-14T18:32:27Z","receivedAt":"2025-08-14T18:32:39Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Aug 14, 2025 at 2:23 PM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> > Since the documentation asserts that the emitted key/value lines will\n> > be sorted lexicographically, can we also have a test that verifies\n> > that behavior?\n>\n> Since we agreed to return the values in the order they were requested,\n> I'll test that order instead:\n\nYep, sounds good.\n\n> test_expect_success 'the values are returned in the same order they were requested' '\n>         printf \"references.format=files\\nlayout.bare=false\\n\" >expected1 &&\n>         printf \"layout.bare=false\\nreferences.format=files\\n\" >expected2 &&\n>         git init --ref-format=files same-order &&\n>         git -C same-order repo info references.format layout.bare >actual1 &&\n>         git -C same-order repo info layout.bare references.format >actual2 &&\n>         test_cmp expected1 actual1 &&\n>         test_cmp expected2 actual2\n> '\n\nRather than the above, I think a more satisfactory and meaningful test would be:\n\n    test_expect_success 'values returned in order requested' '\n        cat >expect <<-\\EOF &&\n        layout.bare=false\n        references.format=files\n        layout.bare=false\n        EOF\n        git init --ref-format=files ordered &&\n        git -C ordered repo info layout.bare references.format\nlayout.bare >actual &&\n        test_cmp expect actual\n    '\n"},{"id":"524185","messageId":"xmqqfrdt3feh.fsf@gitster.g","threadId":"63618","inReplyTo":"CAPig+cTQdEDZduG_UmdHn1JNfj7iTw0=jwdkX+VPJ11Jo0jUGA@mail.gmail.com","subject":"Re: [GSoC PATCH v9 3/5] repo: add the field layout.bare","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-14T18:51:50Z","receivedAt":"2025-08-14T18:51:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> test_expect_success 'the values are returned in the same order they were requested' '\n>>         printf \"references.format=files\\nlayout.bare=false\\n\" >expected1 &&\n>>         printf \"layout.bare=false\\nreferences.format=files\\n\" >expected2 &&\n>>         git init --ref-format=files same-order &&\n>>         git -C same-order repo info references.format layout.bare >actual1 &&\n>>         git -C same-order repo info layout.bare references.format >actual2 &&\n>>         test_cmp expected1 actual1 &&\n>>         test_cmp expected2 actual2\n>> '\n>\n> Rather than the above, I think a more satisfactory and meaningful test would be:\n>\n>     test_expect_success 'values returned in order requested' '\n>         cat >expect <<-\\EOF &&\n>         layout.bare=false\n>         references.format=files\n>         layout.bare=false\n>         EOF\n>         git init --ref-format=files ordered &&\n>         git -C ordered repo info layout.bare references.format\n> layout.bare >actual &&\n>         test_cmp expect actual\n>     '\n\nI do not think the second \"layout.bare\" should be line-wrapped.\n\nYour point that it is more obvious when the expectations are shown\nin HERE-doc may be valid.  Overly long printf with \\n indeed is\nharder to follow.  Even though there is no reason for a real user to\ndo so, asking for the same piece of information twice would\ndemonstrate that there is no deduplication.\n\nI also care about future-proofing, though.  When Git is built with\nWITH_BREAKING_CHANGES=YesPlease, this test would break as the\ndefault reference backend will be reftable in that alternate world,\nwouldn't it?\n"},{"id":"524189","messageId":"CAPig+cSZnfG7NDCGDDnFvtwS4hpcGkmVKA0hPhLALmXj=0Zbbw@mail.gmail.com","threadId":"63618","inReplyTo":"xmqqfrdt3feh.fsf@gitster.g","subject":"Re: [GSoC PATCH v9 3/5] repo: add the field layout.bare","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-14T22:05:36Z","receivedAt":"2025-08-14T22:05:48Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Aug 14, 2025 at 2:51 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Eric Sunshine <sunshine@sunshineco.com> writes:\n> > Rather than the above, I think a more satisfactory and meaningful test would be:\n> >\n> >     test_expect_success 'values returned in order requested' '\n> >         cat >expect <<-\\EOF &&\n> >         layout.bare=false\n> >         references.format=files\n> >         layout.bare=false\n> >         EOF\n> >         git init --ref-format=files ordered &&\n> >         git -C ordered repo info layout.bare references.format\n> > layout.bare >actual &&\n> >         test_cmp expect actual\n> >     '\n>\n> I do not think the second \"layout.bare\" should be line-wrapped.\n\nI typed that command all one one line; Gmail wrapped the line.\n\n> Your point that it is more obvious when the expectations are shown\n> in HERE-doc may be valid.  Overly long printf with \\n indeed is\n> harder to follow.  Even though there is no reason for a real user to\n> do so, asking for the same piece of information twice would\n> demonstrate that there is no deduplication.\n\nYes, part of the point of the illustrated test was indeed to\ndemonstrate lack of deduplication.\n\nBy the way, as a real-world developer/user, I do periodically find\nmyself in situations in which it *is* convenient to ask for the same\npiece of information twice (or thrice) because it simplifies\ndownstream scripting in ad hoc (and not so ad hoc) situations when I\nneed to manipulate the same value in different ways. In such cases,\nasking for the information more than once saves me the trouble of\nhaving to assign the value to a variable, which is handy when the\ndownstream language or tool doesn't provide variables.\n\n> I also care about future-proofing, though.  When Git is built with\n> WITH_BREAKING_CHANGES=YesPlease, this test would break as the\n> default reference backend will be reftable in that alternate world,\n> wouldn't it?\n\nI think Lucas already future-proofed this (and my example copied his\nfuture-proofing) by using `--ref-format=files` with the git-init\ninvocation.\n"},{"id":"524193","messageId":"1ABC1E05-BEE9-4E17-8C6C-8BB4127378BF@gmail.com","threadId":"63618","inReplyTo":"xmqqfrdt3feh.fsf@gitster.g","subject":"Re: [GSoC PATCH v9 3/5] repo: add the field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-14T22:18:28Z","receivedAt":"2025-08-14T22:18:49Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> I also care about future-proofing, though.  When Git is built with\n> WITH_BREAKING_CHANGES=YesPlease, this test would break as the\n> default reference backend will be reftable in that alternate world,\n> wouldn't it?\n\nTo be honest, it wouldn't matter what are the keys selected for\ntesting this behavior. I'm only using references.format because it\nwas the first that I implemented. But given that, I can also change\ntheir order to something like:\n\n[1/5] repo: declare the repo command\n[2/5] repo: add the field layout.bare\n[3/5] repo: add the field layout.shallow\n[2/5] repo: add the field references.format\n[5/5] repo: add the --format flag\n\nThis way, this tests could be placed in 3/5 and using layout.bare and\nlayout.shallow as keys.\n\nThanks!\n"},{"id":"524200","messageId":"CAPig+cSkAqJ23ESdFeUePAuAB=j0N+NsrdEiW4onQpU9Sbki_A@mail.gmail.com","threadId":"63618","inReplyTo":"1ABC1E05-BEE9-4E17-8C6C-8BB4127378BF@gmail.com","subject":"Re: [GSoC PATCH v9 3/5] repo: add the field layout.bare","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-14T23:41:08Z","receivedAt":"2025-08-14T23:41:20Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Aug 14, 2025 at 6:18 PM Lucas Seiki Oshiro\n<lucasseikioshiro@gmail.com> wrote:\n> > I also care about future-proofing, though.  When Git is built with\n> > WITH_BREAKING_CHANGES=YesPlease, this test would break as the\n> > default reference backend will be reftable in that alternate world,\n> > wouldn't it?\n>\n> To be honest, it wouldn't matter what are the keys selected for\n> testing this behavior. I'm only using references.format because it\n> was the first that I implemented. But given that, I can also change\n> their order to something like:\n>\n> [1/5] repo: declare the repo command\n> [2/5] repo: add the field layout.bare\n> [3/5] repo: add the field layout.shallow\n> [2/5] repo: add the field references.format\n> [5/5] repo: add the --format flag\n>\n> This way, this tests could be placed in 3/5 and using layout.bare and\n> layout.shallow as keys.\n\nI don't have a strong preference since I think you already\nfuture-proofed the test by using `--ref-format=files` with the\ngit-init\ninvocation, but the above suggested patch order would work, as well,\nand seems sufficiently reviewer-friendly.\n"},{"id":"524203","messageId":"xmqqectdv0rz.fsf@gitster.g","threadId":"63618","inReplyTo":"CAPig+cSZnfG7NDCGDDnFvtwS4hpcGkmVKA0hPhLALmXj=0Zbbw@mail.gmail.com","subject":"Re: [GSoC PATCH v9 3/5] repo: add the field layout.bare","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-15T01:20:16Z","receivedAt":"2025-08-15T01:20:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> I also care about future-proofing, though.  When Git is built with\n>> WITH_BREAKING_CHANGES=YesPlease, this test would break as the\n>> default reference backend will be reftable in that alternate world,\n>> wouldn't it?\n>\n> I think Lucas already future-proofed this (and my example copied his\n> future-proofing) by using `--ref-format=files` with the git-init\n> invocation.\n\nAh, OK, if we are forcing a specific format, then we are already\nfuture-proofed, especially when the format is something that will be\nwith us forever, like the files backend.\n\nThanks.\n"},{"id":"524246","messageId":"20250815135521.44044-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v10 0/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-15T13:55:16Z","receivedAt":"2025-08-15T15:29:53Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\nThe only major change in this v10 is that git-repo-info now returns the\nvalues following the same order as they were requested instead of\nsorting the keys. In past versions, the sorting existed for dropping\nduplicated keys, but after the discussion with Eric, Phillip and Junio,\nwe agreed that returning the values following the keys order was more\nimportant than removing the duplications, which weren't exactly a bad\nthing. Consequently, the duplication checks were also removed.\n\nOther minor issues pointed by Eric were also addressed.\n\nThanks!\n\nHere's the range-diff versus v9:\n\n1:  3c2ede66be = 1:  6767028da3 repo: declare the repo command\n2:  b18e74763d ! 2:  c44e2d1619 repo: add the field references.format\n    @@ Documentation/git-repo.adoc: COMMANDS\n      \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n      \tsection below).\n     ++\n    -+The returned data is lexicographically sorted by the keys.\n    ++The values are returned in the same order in which their respective keys were\n    ++requested.\n     ++\n     +The output format consists of key-value pairs one per line using the `=`\n     +character as the delimiter between the key and the value. Values containing\n     +\"unusual\" characters are quoted as explained for the configuration variable\n    -+`core.quotePath` (see linkgit:git-config[1]). This is the default.\n    ++`core.quotePath` (see linkgit:git-config[1]).\n     +\n     +INFO KEYS\n     +---------\n    @@ builtin/repo.c\n      \treturn 0;\n      }\n      \n    -+/* repo_info_fields keys should be in lexicographical order */\n    ++/* repo_info_fields keys must be in lexicographical order */\n     +static const struct field repo_info_fields[] = {\n     +\t{ \"references.format\", get_references_format },\n     +};\n    @@ builtin/repo.c\n     +\treturn found ? found->get_value : NULL;\n     +}\n     +\n    -+static int qsort_strcmp(const void *va, const void *vb)\n    -+{\n    -+\tconst char *a = *(const char **)va;\n    -+\tconst char *b = *(const char **)vb;\n    -+\n    -+\treturn strcmp(a, b);\n    -+}\n    -+\n     +static int print_fields(int argc, const char **argv, struct repository *repo)\n     +{\n     +\tint ret = 0;\n    -+\tconst char *last = \"\";\n     +\tstruct strbuf valbuf = STRBUF_INIT;\n     +\tstruct strbuf quotbuf = STRBUF_INIT;\n     +\n    -+\tQSORT(argv, argc, qsort_strcmp);\n    -+\n     +\tfor (int i = 0; i < argc; i++) {\n     +\t\tget_value_fn *get_value;\n     +\t\tconst char *key = argv[i];\n     +\n    -+\t\tstrbuf_reset(&valbuf);\n    -+\t\tstrbuf_reset(&quotbuf);\n    -+\n    -+\t\tif (!strcmp(key, last))\n    -+\t\t\tcontinue;\n    -+\n    -+\t\tlast = key;\n     +\t\tget_value = get_value_fn_for_key(key);\n     +\n     +\t\tif (!get_value) {\n    @@ builtin/repo.c\n     +\t\t\tcontinue;\n     +\t\t}\n     +\n    ++\t\tstrbuf_reset(&valbuf);\n    ++\t\tstrbuf_reset(&quotbuf);\n    ++\n     +\t\tget_value(repo, &valbuf);\n     +\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n     +\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n    @@ t/t1900-repo.sh (new)\n     +\n     +# Test whether a key-value pair is correctly returned\n     +#\n    -+# Usage: test_repo_info <label> <init command> <key> <expected value>\n    ++# Usage: test_repo_info <label> <init command> <repo_name> <key> <expected value>\n     +#\n     +# Arguments:\n     +#   label: the label of the test\n    @@ t/t1900-repo.sh (new)\n     +\t'\n     +}\n     +\n    -+test_repo_info 'ref format files is retrieved correctly' '\n    -+\tgit init --ref-format=files' 'format-files' 'references.format' 'files'\n    ++test_repo_info 'ref format files is retrieved correctly' \\\n    ++\t'git init --ref-format=files' 'format-files' 'references.format' 'files'\n     +\n    -+test_repo_info 'ref format reftable is retrieved correctly' '\n    -+\tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n    ++test_repo_info 'ref format reftable is retrieved correctly' \\\n    ++\t'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n     +\n     +test_expect_success 'git-repo-info fails if an invalid key is requested' '\n     +\techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n    @@ t/t1900-repo.sh (new)\n     +\ttest_cmp expected actual\n     +'\n     +\n    -+test_expect_success 'only one value is returned if the same key is requested twice' '\n    -+\tval=$(git rev-parse --show-ref-format) &&\n    -+\techo \"references.format=$val\" >expect &&\n    -+\tgit repo info references.format references.format >actual &&\n    -+\ttest_cmp expect actual\n    -+'\n    -+\n     +test_done\n3:  35916b210e ! 3:  e3009a85e1 repo: add the field layout.bare\n    @@ builtin/repo.c: struct field {\n      \tstrbuf_addstr(buf,\n     @@ builtin/repo.c: static int get_references_format(struct repository *repo, struct strbuf *buf)\n      \n    - /* repo_info_fields keys should be in lexicographical order */\n    + /* repo_info_fields keys must be in lexicographical order */\n      static const struct field repo_info_fields[] = {\n     +\t{ \"layout.bare\", get_layout_bare },\n      \t{ \"references.format\", get_references_format },\n    @@ builtin/repo.c: static int get_references_format(struct repository *repo, struct\n      \n     \n      ## t/t1900-repo.sh ##\n    -@@ t/t1900-repo.sh: test_repo_info 'ref format files is retrieved correctly' '\n    - test_repo_info 'ref format reftable is retrieved correctly' '\n    - \tgit init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n    +@@ t/t1900-repo.sh: test_repo_info 'ref format files is retrieved correctly' \\\n    + test_repo_info 'ref format reftable is retrieved correctly' \\\n    + \t'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n      \n     +test_repo_info 'bare repository = false is retrieved correctly' \\\n     +\t'git init' 'nonbare' 'layout.bare' 'false'\n    @@ t/t1900-repo.sh: test_repo_info 'ref format files is retrieved correctly' '\n     +test_repo_info 'bare repository = true is retrieved correctly' \\\n     +\t'git init --bare' 'bare' 'layout.bare' 'true'\n     +\n    - test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    - \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n    - \ttest_must_fail git repo info foo 2>actual_err &&\n    -@@ t/t1900-repo.sh: test_expect_success 'only one value is returned if the same key is requested twi\n    - \ttest_cmp expect actual\n    - '\n    - \n    -+test_expect_success 'output is returned correctly when two keys are requested' '\n    -+\tcat >expected <<-\\EOF &&\n    ++test_expect_success 'values returned in order requested' '\n    ++\tcat >expect <<-\\EOF &&\n     +\tlayout.bare=false\n     +\treferences.format=files\n    ++\tlayout.bare=false\n     +\tEOF\n    -+\tgit init --ref-format=files two-keys &&\n    -+\tgit -C two-keys repo info layout.bare references.format >actual &&\n    -+\ttest_cmp expected actual\n    ++\tgit init --ref-format=files ordered &&\n    ++\tgit -C ordered repo info layout.bare references.format layout.bare >actual &&\n    ++\ttest_cmp expect actual\n     +'\n     +\n    - test_done\n    + test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    + \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n    + \ttest_must_fail git repo info foo 2>actual_err &&\n4:  91fc5c4e50 ! 4:  3837899c32 repo: add the field layout.shallow\n    @@ builtin/repo.c: static int get_layout_bare(struct repository *repo UNUSED, struc\n      {\n      \tstrbuf_addstr(buf,\n     @@ builtin/repo.c: static int get_references_format(struct repository *repo, struct strbuf *buf)\n    - /* repo_info_fields keys should be in lexicographical order */\n    + /* repo_info_fields keys must be in lexicographical order */\n      static const struct field repo_info_fields[] = {\n      \t{ \"layout.bare\", get_layout_bare },\n     +\t{ \"layout.shallow\", get_layout_shallow },\n    @@ t/t1900-repo.sh: test_repo_info 'bare repository = false is retrieved correctly'\n     +test_repo_info 'shallow repository = false is retrieved correctly' \\\n     +\t'git init' 'nonshallow' 'layout.shallow' 'false'\n     +\n    -+test_repo_info 'shallow repository = true is retrieved correctly' \\\n    -+\t'git init remote &&\n    ++test_expect_success 'setup remote' '\n    ++\tgit init remote &&\n     +\techo x >remote/x &&\n     +\tgit -C remote add x &&\n    -+\tgit -C remote commit -m x &&\n    -+\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n    ++\tgit -C remote commit -m x\n    ++'\n    ++\n    ++test_repo_info 'shallow repository = true is retrieved correctly' \\\n    ++\t'git clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n     +\n    - test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    - \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n    - \ttest_must_fail git repo info foo 2>actual_err &&\n    + test_expect_success 'values returned in order requested' '\n    + \tcat >expect <<-\\EOF &&\n    + \tlayout.bare=false\n5:  8af32d7066 ! 5:  19fdfce646 repo: add the --format flag\n    @@ Documentation/git-repo.adoc: THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHAN\n      \tRetrieve metadata-related information about the current repository. Only\n      \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n      \tsection below).\n    - +\n    - The returned data is lexicographically sorted by the keys.\n    +@@ Documentation/git-repo.adoc: COMMANDS\n    + The values are returned in the same order in which their respective keys were\n    + requested.\n      +\n     -The output format consists of key-value pairs one per line using the `=`\n     -character as the delimiter between the key and the value. Values containing\n     -\"unusual\" characters are quoted as explained for the configuration variable\n    +-`core.quotePath` (see linkgit:git-config[1]).\n     +The output format can be chosen through the flag `--format`. Two formats are\n     +supported:\n     ++\n     +* `keyvalue`: output key-value pairs one per line using the `=` character as\n     +the delimiter between the key and the value. Values containing \"unusual\"\n     +characters are quoted as explained for the configuration variable\n    - `core.quotePath` (see linkgit:git-config[1]). This is the default.\n    - \n    ++`core.quotePath` (see linkgit:git-config[1]). This is the default.\n    ++\n     +* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n     +between the key and the value and using a null character after each value.\n     +This format is better suited for being parsed by another applications than\n     +`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n    -+\n    + \n      INFO KEYS\n      ---------\n     -\n    @@ builtin/repo.c\n      struct field {\n      \tconst char *key;\n      \tget_value_fn *get_value;\n    -@@ builtin/repo.c: static int qsort_strcmp(const void *va, const void *vb)\n    - \treturn strcmp(a, b);\n    +@@ builtin/repo.c: static get_value_fn *get_value_fn_for_key(const char *key)\n    + \treturn found ? found->get_value : NULL;\n      }\n      \n     -static int print_fields(int argc, const char **argv, struct repository *repo)\n    @@ builtin/repo.c: static int qsort_strcmp(const void *va, const void *vb)\n     +\t\t\tenum output_format format)\n      {\n      \tint ret = 0;\n    - \tconst char *last = \"\";\n    + \tstruct strbuf valbuf = STRBUF_INIT;\n     @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repository *repo)\n    - \t\t}\n    + \t\tstrbuf_reset(&quotbuf);\n      \n      \t\tget_value(repo, &valbuf);\n     -\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n    @@ t/t1900-repo.sh: test_repo_info () {\n     +\t'\n      }\n      \n    - test_repo_info 'ref format files is retrieved correctly' '\n    -@@ t/t1900-repo.sh: test_repo_info 'bare repository = true is retrieved correctly' \\\n    - test_repo_info 'shallow repository = false is retrieved correctly' \\\n    - \t'git init' 'nonshallow' 'layout.shallow' 'false'\n    - \n    --test_repo_info 'shallow repository = true is retrieved correctly' \\\n    --\t'git init remote &&\n    -+test_expect_success 'setup remote' '\n    -+\tgit init remote &&\n    - \techo x >remote/x &&\n    - \tgit -C remote add x &&\n    --\tgit -C remote commit -m x &&\n    --\tgit clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n    -+\tgit -C remote commit -m x\n    -+'\n    -+\n    -+test_repo_info 'shallow repository = true is retrieved correctly' \\\n    -+\t'git clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n    - \n    - test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    - \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n    -@@ t/t1900-repo.sh: test_expect_success 'output is returned correctly when two keys are requested' '\n    + test_repo_info 'ref format files is retrieved correctly' \\\n    +@@ t/t1900-repo.sh: test_expect_success 'git-repo-info outputs data even if there is an invalid fiel\n      \ttest_cmp expected actual\n      '\n      \n     +test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n    -+\techo \"fatal: invalid format '\\'foo\\''\" >expected &&\n    ++\techo \"fatal: invalid format ${SQ}foo${SQ}\" >expected &&\n     +\ttest_must_fail git repo info --format=foo 2>err &&\n     +\ttest_cmp expected err\n     +'\n\n\nLucas Seiki Oshiro (5):\n  repo: declare the repo command\n  repo: add the field references.format\n  repo: add the field layout.bare\n  repo: add the field layout.shallow\n  repo: add the --format flag\n\n .gitignore                  |   1 +\n Documentation/git-repo.adoc |  82 ++++++++++++++++++++\n Documentation/meson.build   |   1 +\n Makefile                    |   1 +\n builtin.h                   |   1 +\n builtin/repo.c              | 150 ++++++++++++++++++++++++++++++++++++\n command-list.txt            |   1 +\n git.c                       |   1 +\n meson.build                 |   1 +\n t/meson.build               |   1 +\n t/t1900-repo.sh             |  96 +++++++++++++++++++++++\n 11 files changed, 336 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n create mode 100755 t/t1900-repo.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524247","messageId":"20250815135521.44044-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250815135521.44044-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v10 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-15T13:55:17Z","receivedAt":"2025-08-15T15:29:56Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, `git rev-parse` covers a wide range of functionality not\ndirectly related to parsing revisions, as its name suggests. Over time,\nmany features like parsing datestrings, options, paths, and others\nwere added to it because there wasn't a more appropriate command\nto place them.\n\nCreate a new Git command called `repo`. `git repo` will be the main\ncommand for obtaining the information about a repository (such as\nmetadata and metrics).\n\nAlso declare a subcommand for `repo` called `info`. `git repo info`\nwill bring the functionality of retrieving repository-related\ninformation currently returned by `rev-parse`.\n\nAdd the required documentation and build changes to enable usage of\nthis subcommand.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore                  |  1 +\n Documentation/git-repo.adoc | 32 ++++++++++++++++++++++++++++++++\n Documentation/meson.build   |  1 +\n Makefile                    |  1 +\n builtin.h                   |  1 +\n builtin/repo.c              | 27 +++++++++++++++++++++++++++\n command-list.txt            |  1 +\n git.c                       |  1 +\n meson.build                 |  1 +\n 9 files changed, 66 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..1803023427 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nnew file mode 100644\nindex 0000000000..68c706f5a0\n--- /dev/null\n+++ b/Documentation/git-repo.adoc\n@@ -0,0 +1,32 @@\n+git-repo(1)\n+===========\n+\n+NAME\n+----\n+git-repo - Retrieve information about the repository\n+\n+SYNOPSIS\n+--------\n+[synopsis]\n+git repo info [<key>...]\n+\n+DESCRIPTION\n+-----------\n+Retrieve information about the repository.\n+\n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n+COMMANDS\n+--------\n+`info [<key>...]`::\n+\tRetrieve metadata-related information about the current repository. Only\n+\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n+\tsection below).\n+\n+SEE ALSO\n+--------\n+linkgit:git-rev-parse[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 4404c623f0..41f43e0336 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -116,6 +116,7 @@ manpages = {\n   'git-repack.adoc' : 1,\n   'git-replace.adoc' : 1,\n   'git-replay.adoc' : 1,\n+  'git-repo.adoc' : 1,\n   'git-request-pull.adoc' : 1,\n   'git-rerere.adoc' : 1,\n   'git-reset.adoc' : 1,\ndiff --git a/Makefile b/Makefile\nindex e11340c1ae..ec7ac58980 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1306,6 +1306,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..e6458e6fb9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo.c b/builtin/repo.c\nnew file mode 100644\nindex 0000000000..fd2a9b4216\n--- /dev/null\n+++ b/builtin/repo.c\n@@ -0,0 +1,27 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+static const char *const repo_usage[] = {\n+\t\"git repo info [<key>...]\",\n+\tNULL\n+};\n+\n+static int repo_info(int argc UNUSED, const char **argv UNUSED,\n+\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+{\n+\treturn 0;\n+}\n+\n+int cmd_repo(int argc, const char **argv, const char *prefix,\n+\t     struct repository *repo)\n+{\n+\tparse_opt_subcommand_fn *fn = NULL;\n+\tstruct option options[] = {\n+\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\treturn fn(argc, argv, prefix, repo);\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex b7ade3ab9f..1b0bdee00d 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -164,6 +164,7 @@ git-remote                              ancillarymanipulators           complete\n git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-replay                              plumbingmanipulators\n+git-repo                                plumbinginterrogators\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           history\ndiff --git a/git.c b/git.c\nindex 83eac0aeab..d4ff4d5517 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo\", cmd_repo, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 5dd299b496..e8ec0eca16 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524248","messageId":"20250815135521.44044-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250815135521.44044-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v10 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-15T13:55:18Z","receivedAt":"2025-08-15T15:30:00Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--show-ref-format` from git-rev-parse is used for retrieving\nthe reference format (i.e. `files` or `reftable`). This way, it is\nused for querying repository metadata, fitting in the purpose of\ngit-repo-info.\n\nAdd a new field `references.format` to the repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 20 ++++++++++\n builtin/repo.c              | 74 ++++++++++++++++++++++++++++++++++++-\n t/meson.build               |  1 +\n t/t1900-repo.sh             | 50 +++++++++++++++++++++++++\n 4 files changed, 143 insertions(+), 2 deletions(-)\n create mode 100755 t/t1900-repo.sh\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 68c706f5a0..a708c70a3d 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -22,6 +22,26 @@ COMMANDS\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n++\n+The values are returned in the same order in which their respective keys were\n+requested.\n++\n+The output format consists of key-value pairs one per line using the `=`\n+character as the delimiter between the key and the value. Values containing\n+\"unusual\" characters are quoted as explained for the configuration variable\n+`core.quotePath` (see linkgit:git-config[1]).\n+\n+INFO KEYS\n+---------\n+\n+In order to obtain a set of values from `git repo info`, you should provide\n+the keys that identify them. Here's a list of the available keys and the\n+values that they return:\n+\n+`references.format`::\n+The reference storage format. The valid values are:\n++\n+include::ref-storage-format.adoc[]\n \n SEE ALSO\n --------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex fd2a9b4216..73d4e27a16 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,17 +1,87 @@\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"quote.h\"\n+#include \"refs.h\"\n+#include \"strbuf.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n \tNULL\n };\n \n-static int repo_info(int argc UNUSED, const char **argv UNUSED,\n-\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n+\n+struct field {\n+\tconst char *key;\n+\tget_value_fn *get_value;\n+};\n+\n+static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n+\tstrbuf_addstr(buf,\n+\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n \treturn 0;\n }\n \n+/* repo_info_fields keys must be in lexicographical order */\n+static const struct field repo_info_fields[] = {\n+\t{ \"references.format\", get_references_format },\n+};\n+\n+static int repo_info_fields_cmp(const void *va, const void *vb)\n+{\n+\tconst struct field *a = va;\n+\tconst struct field *b = vb;\n+\n+\treturn strcmp(a->key, b->key);\n+}\n+\n+static get_value_fn *get_value_fn_for_key(const char *key)\n+{\n+\tconst struct field search_key = { key, NULL };\n+\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n+\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n+\t\t\t\t\t    sizeof(*found),\n+\t\t\t\t\t    repo_info_fields_cmp);\n+\treturn found ? found->get_value : NULL;\n+}\n+\n+static int print_fields(int argc, const char **argv, struct repository *repo)\n+{\n+\tint ret = 0;\n+\tstruct strbuf valbuf = STRBUF_INIT;\n+\tstruct strbuf quotbuf = STRBUF_INIT;\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tget_value_fn *get_value;\n+\t\tconst char *key = argv[i];\n+\n+\t\tget_value = get_value_fn_for_key(key);\n+\n+\t\tif (!get_value) {\n+\t\t\tret = error(_(\"key '%s' not found\"), key);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tstrbuf_reset(&valbuf);\n+\t\tstrbuf_reset(&quotbuf);\n+\n+\t\tget_value(repo, &valbuf);\n+\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n+\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\t}\n+\n+\tstrbuf_release(&valbuf);\n+\tstrbuf_release(&quotbuf);\n+\treturn ret;\n+}\n+\n+static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+\t\t     struct repository *repo)\n+{\n+\treturn print_fields(argc - 1, argv + 1, repo);\n+}\n+\n int cmd_repo(int argc, const char **argv, const char *prefix,\n \t     struct repository *repo)\n {\ndiff --git a/t/meson.build b/t/meson.build\nindex 983245501c..7555d52917 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -231,6 +231,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nnew file mode 100755\nindex 0000000000..dca4023a00\n--- /dev/null\n+++ b/t/t1900-repo.sh\n@@ -0,0 +1,50 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+\n+. ./test-lib.sh\n+\n+# Test whether a key-value pair is correctly returned\n+#\n+# Usage: test_repo_info <label> <init command> <repo_name> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init_command: a command which creates a repository\n+#   repo_name: the name of the repository that will be created in init_command\n+#   key: the key of the field that is being tested\n+#   expected_value: the value that the field should contain\n+test_repo_info () {\n+\tlabel=$1\n+\tinit_command=$2\n+\trepo_name=$3\n+\tkey=$4\n+\texpected_value=$5\n+\n+\ttest_expect_success \"$label\" '\n+\t\teval \"$init_command $repo_name\" &&\n+\t\techo \"$key=$expected_value\" >expected &&\n+\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n+\n+test_repo_info 'ref format files is retrieved correctly' \\\n+\t'git init --ref-format=files' 'format-files' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' \\\n+\t'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n+\n+test_expect_success 'git-repo-info fails if an invalid key is requested' '\n+\techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n+\ttest_must_fail git repo info foo 2>actual_err &&\n+\ttest_cmp expected_err actual_err\n+'\n+\n+test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n+\techo \"references.format=$(test_detect_ref_format)\" >expected &&\n+\ttest_must_fail git repo info foo references.format bar >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524249","messageId":"20250815135521.44044-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250815135521.44044-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v10 3/5] repo: add the field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-15T13:55:19Z","receivedAt":"2025-08-15T15:30:04Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag --is-bare-repository from git-rev-parse is used for retrieving\nwhether the current repository is bare. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field layout.bare to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              | 10 ++++++++++\n t/t1900-repo.sh             | 17 +++++++++++++++++\n 3 files changed, 30 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex a708c70a3d..d26c01a21b 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -38,6 +38,9 @@ In order to obtain a set of values from `git repo info`, you should provide\n the keys that identify them. Here's a list of the available keys and the\n values that they return:\n \n+`layout.bare`::\n+`true` if this is a bare repository, otherwise `false`.\n+\n `references.format`::\n The reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 73d4e27a16..aada476e1c 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,4 +1,7 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n+#include \"environment.h\"\n #include \"parse-options.h\"\n #include \"quote.h\"\n #include \"refs.h\"\n@@ -16,6 +19,12 @@ struct field {\n \tget_value_fn *get_value;\n };\n \n+static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf, is_bare_repository() ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -25,6 +34,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n \n /* repo_info_fields keys must be in lexicographical order */\n static const struct field repo_info_fields[] = {\n+\t{ \"layout.bare\", get_layout_bare },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex dca4023a00..8368a168b6 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -35,6 +35,23 @@ test_repo_info 'ref format files is retrieved correctly' \\\n test_repo_info 'ref format reftable is retrieved correctly' \\\n \t'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' \\\n+\t'git init' 'nonbare' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' \\\n+\t'git init --bare' 'bare' 'layout.bare' 'true'\n+\n+test_expect_success 'values returned in order requested' '\n+\tcat >expect <<-\\EOF &&\n+\tlayout.bare=false\n+\treferences.format=files\n+\tlayout.bare=false\n+\tEOF\n+\tgit init --ref-format=files ordered &&\n+\tgit -C ordered repo info layout.bare references.format layout.bare >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n \ttest_must_fail git repo info foo 2>actual_err &&\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524250","messageId":"20250815135521.44044-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250815135521.44044-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v10 4/5] repo: add the field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-15T13:55:20Z","receivedAt":"2025-08-15T15:30:08Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--is-shallow-repository` from git-rev-parse is used for\nretrieving whether the repository is shallow. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field `layout.shallow` to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              |  9 +++++++++\n t/t1900-repo.sh             | 13 +++++++++++++\n 3 files changed, 25 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex d26c01a21b..3231a93947 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -41,6 +41,9 @@ values that they return:\n `layout.bare`::\n `true` if this is a bare repository, otherwise `false`.\n \n+`layout.shallow`::\n+`true` if this is a shallow repository, otherwise `false`.\n+\n `references.format`::\n The reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex aada476e1c..3c9140593b 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -6,6 +6,7 @@\n #include \"quote.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n+#include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n@@ -25,6 +26,13 @@ static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_layout_shallow(struct repository *repo, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf,\n+\t\t      is_repository_shallow(repo) ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -35,6 +43,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n /* repo_info_fields keys must be in lexicographical order */\n static const struct field repo_info_fields[] = {\n \t{ \"layout.bare\", get_layout_bare },\n+\t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 8368a168b6..78766a3f4f 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -41,6 +41,19 @@ test_repo_info 'bare repository = false is retrieved correctly' \\\n test_repo_info 'bare repository = true is retrieved correctly' \\\n \t'git init --bare' 'bare' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' \\\n+\t'git init' 'nonshallow' 'layout.shallow' 'false'\n+\n+test_expect_success 'setup remote' '\n+\tgit init remote &&\n+\techo x >remote/x &&\n+\tgit -C remote add x &&\n+\tgit -C remote commit -m x\n+'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' \\\n+\t'git clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n+\n test_expect_success 'values returned in order requested' '\n \tcat >expect <<-\\EOF &&\n \tlayout.bare=false\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524251","messageId":"20250815135521.44044-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250815135521.44044-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v10 5/5] repo: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-15T13:55:21Z","receivedAt":"2025-08-15T15:30:11Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the --format flag to git-repo-info. By using this flag, the users\ncan choose the format for obtaining the data they requested.\n\nGiven that this command can be used for generating input for other\napplications and for being read by end users, it requires at least two\nformats: one for being read by humans and other for being read by\nmachines. Some other Git commands also have two output formats, notably\ngit-config which was the inspiration for the two formats that were\nchosen here:\n\n- keyvalue, where the retrieved data is printed one per line, using =\n  for delimiting the key and the value. This is the default format,\n  targeted for end users.\n- nul, where the retrieved data is separated by null characters, using\n  the newline character for delimiting the key and the value. This\n  format is targeted for being read by machines.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 38 ++++++++++++++++++++++++------\n builtin/repo.c              | 46 ++++++++++++++++++++++++++++++++-----\n t/t1900-repo.sh             | 24 +++++++++++++++----\n 3 files changed, 91 insertions(+), 17 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 3231a93947..cb22f7ef65 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -8,7 +8,7 @@ git-repo - Retrieve information about the repository\n SYNOPSIS\n --------\n [synopsis]\n-git repo info [<key>...]\n+git repo info [--format=(keyvalue|nul)] [<key>...]\n \n DESCRIPTION\n -----------\n@@ -18,7 +18,7 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n \n COMMANDS\n --------\n-`info [<key>...]`::\n+`info [--format=(keyvalue|nul)] [<key>...]`::\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n@@ -26,14 +26,21 @@ COMMANDS\n The values are returned in the same order in which their respective keys were\n requested.\n +\n-The output format consists of key-value pairs one per line using the `=`\n-character as the delimiter between the key and the value. Values containing\n-\"unusual\" characters are quoted as explained for the configuration variable\n-`core.quotePath` (see linkgit:git-config[1]).\n+The output format can be chosen through the flag `--format`. Two formats are\n+supported:\n++\n+* `keyvalue`: output key-value pairs one per line using the `=` character as\n+the delimiter between the key and the value. Values containing \"unusual\"\n+characters are quoted as explained for the configuration variable\n+`core.quotePath` (see linkgit:git-config[1]). This is the default.\n+\n+* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n+between the key and the value and using a null character after each value.\n+This format is better suited for being parsed by another applications than\n+`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n \n INFO KEYS\n ---------\n-\n In order to obtain a set of values from `git repo info`, you should provide\n the keys that identify them. Here's a list of the available keys and the\n values that they return:\n@@ -49,6 +56,23 @@ The reference storage format. The valid values are:\n +\n include::ref-storage-format.adoc[]\n \n+EXAMPLES\n+--------\n+\n+* Retrieves the reference format of the current repository:\n++\n+------------\n+git repo info references.format\n+------------\n++\n+\n+* Retrieves whether the current repository is bare and whether it is shallow\n+using the `nul` format:\n++\n+------------\n+git repo info --format=nul layout.bare layout.shallow\n+------------\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 3c9140593b..8c6e7f42ab 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -9,12 +9,17 @@\n #include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n-\t\"git repo info [<key>...]\",\n+\t\"git repo info [--format=(keyvalue|nul)] [<key>...]\",\n \tNULL\n };\n \n typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n \n+enum output_format {\n+\tFORMAT_KEYVALUE,\n+\tFORMAT_NUL_TERMINATED,\n+};\n+\n struct field {\n \tconst char *key;\n \tget_value_fn *get_value;\n@@ -65,7 +70,9 @@ static get_value_fn *get_value_fn_for_key(const char *key)\n \treturn found ? found->get_value : NULL;\n }\n \n-static int print_fields(int argc, const char **argv, struct repository *repo)\n+static int print_fields(int argc, const char **argv,\n+\t\t\tstruct repository *repo,\n+\t\t\tenum output_format format)\n {\n \tint ret = 0;\n \tstruct strbuf valbuf = STRBUF_INIT;\n@@ -86,8 +93,18 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \t\tstrbuf_reset(&quotbuf);\n \n \t\tget_value(repo, &valbuf);\n-\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n-\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\n+\t\tswitch (format) {\n+\t\tcase FORMAT_KEYVALUE:\n+\t\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n+\t\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\t\t\tbreak;\n+\t\tcase FORMAT_NUL_TERMINATED:\n+\t\t\tprintf(\"%s\\n%s%c\", key, valbuf.buf, '\\0');\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"not a valid output format: %d\", format);\n+\t\t}\n \t}\n \n \tstrbuf_release(&valbuf);\n@@ -95,10 +112,27 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \treturn ret;\n }\n \n-static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+static int repo_info(int argc, const char **argv, const char *prefix,\n \t\t     struct repository *repo)\n {\n-\treturn print_fields(argc - 1, argv + 1, repo);\n+\tconst char *format_str = \"keyvalue\";\n+\tenum output_format format;\n+\tstruct option options[] = {\n+\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n+\t\t\t   N_(\"output format\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\tif (!strcmp(format_str, \"keyvalue\"))\n+\t\tformat = FORMAT_KEYVALUE;\n+\telse if (!strcmp(format_str, \"nul\"))\n+\t\tformat = FORMAT_NUL_TERMINATED;\n+\telse\n+\t\tdie(_(\"invalid format '%s'\"), format_str);\n+\n+\treturn print_fields(argc, argv, repo, format);\n }\n \n int cmd_repo(int argc, const char **argv, const char *prefix,\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 78766a3f4f..a4ec6b42df 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -21,12 +21,22 @@ test_repo_info () {\n \tkey=$4\n \texpected_value=$5\n \n-\ttest_expect_success \"$label\" '\n-\t\teval \"$init_command $repo_name\" &&\n-\t\techo \"$key=$expected_value\" >expected &&\n-\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\trepo_name_keyvalue=\"$repo_name\"-keyvalue\n+\trepo_name_nul=\"$repo_name\"-nul\n+\n+\ttest_expect_success \"keyvalue: $label\" '\n+\t\teval \"$init_command $repo_name_keyvalue\" &&\n+\t\techo \"$key=$expected_value\" > expected &&\n+\t\tgit -C \"$repo_name_keyvalue\" repo info \"$key\" >actual &&\n \t\ttest_cmp expected actual\n \t'\n+\n+\ttest_expect_success \"nul: $label\" '\n+\t\teval \"$init_command $repo_name_nul\" &&\n+\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n+\t\tgit -C \"$repo_name_nul\" repo info --format=nul \"$key\" >actual &&\n+\t\ttest_cmp_bin expected actual\n+\t'\n }\n \n test_repo_info 'ref format files is retrieved correctly' \\\n@@ -77,4 +87,10 @@ test_expect_success 'git-repo-info outputs data even if there is an invalid fiel\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n+\techo \"fatal: invalid format ${SQ}foo${SQ}\" >expected &&\n+\ttest_must_fail git repo info --format=foo 2>err &&\n+\ttest_cmp expected err\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524272","messageId":"xmqqfrdsqvi1.fsf@gitster.g","threadId":"63618","inReplyTo":"20250815135521.44044-3-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v10 2/5] repo: add the field references.format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-15T18:40:06Z","receivedAt":"2025-08-15T18:44:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> new file mode 100755\n> index 0000000000..dca4023a00\n> --- /dev/null\n> +++ b/t/t1900-repo.sh\n> @@ -0,0 +1,50 @@\n> +#!/bin/sh\n> +\n> +test_description='test git repo-info'\n> +\n> +. ./test-lib.sh\n> +\n> +# Test whether a key-value pair is correctly returned\n> +#\n> +# Usage: test_repo_info <label> <init command> <repo_name> <key> <expected value>\n> +#\n> +# Arguments:\n> +#   label: the label of the test\n> +#   init_command: a command which creates a repository\n> +#   repo_name: the name of the repository that will be created in init_command\n> +#   key: the key of the field that is being tested\n> +#   expected_value: the value that the field should contain\n> +test_repo_info () {\n> +\tlabel=$1\n> +\tinit_command=$2\n> +\trepo_name=$3\n> +\tkey=$4\n> +\texpected_value=$5\n> +\n> +\ttest_expect_success \"$label\" '\n> +\t\teval \"$init_command $repo_name\" &&\n> +\t\techo \"$key=$expected_value\" >expected &&\n> +\t\tgit -C $repo_name repo info \"$key\" >actual &&\n\nMost tests compare \"expect\" and \"actual\" by convention, so this is a\nbit unusual, but I'll let it go.\n\nI know it is tempting to write a helper function like this that\nlooks overly generic, but I've seen that we end up either tests that\nare too hard to modify (because such helpers are not flexible enough)\nor we end up adding more helpers to cover different cases, and\neither case the presense of the helper does not help the resulting\ntests easier to read or modify.\n\nIf this were \"test repo-info for ref.format only\" that takes a\nsingle parameter (format), that might have been more palatable, but\nI'll withhold my judgement until we have tests to handle more keys\nin later steps.\n\n> +\t\ttest_cmp expected actual\n> +\t'\n> +}\n> +\n> +test_repo_info 'ref format files is retrieved correctly' \\\n> +\t'git init --ref-format=files' 'format-files' 'references.format' 'files'\n> +\n> +test_repo_info 'ref format reftable is retrieved correctly' \\\n> +\t'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n> +\n> +test_expect_success 'git-repo-info fails if an invalid key is requested' '\n> +\techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n> +\ttest_must_fail git repo info foo 2>actual_err &&\n> +\ttest_cmp expected_err actual_err\n> +'\n\nAlso, unless we capture both standard output (in actual) and\nstandard error (in actual_err) from the command being tested, and\ninstead are interested in testing only what comes out of the error\nstream, it is more common to use the usual \"expect vs actual\".\n\n> +\n> +test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n> +\techo \"references.format=$(test_detect_ref_format)\" >expected &&\n> +\ttest_must_fail git repo info foo references.format bar >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_done\n"},{"id":"524273","messageId":"5F0AC8F9-8B0A-457E-80D9-0AAC07D6CB41@gmail.com","threadId":"63618","inReplyTo":"xmqqfrdsqvi1.fsf@gitster.g","subject":"Re: [GSoC PATCH v10 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-15T19:12:13Z","receivedAt":"2025-08-15T19:12:31Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"\n> I know it is tempting to write a helper function like this that\n> looks overly generic, but I've seen that we end up either tests that\n> are too hard to modify (because such helpers are not flexible enough)\n> or we end up adding more helpers to cover different cases, and\n> either case the presense of the helper does not help the resulting\n> tests easier to read or modify.\n\nI understand... But in this case, most of the tests would look almost\nthe same and there will be a lot of code duplication, since I'm\ntesting both output formats (not in this patch, but in 5/5).\n\n> If this were \"test repo-info for ref.format only\" that takes a\n> single parameter (format), that might have been more palatable, but\n> I'll withhold my judgement until we have tests to handle more keys\n> in later steps.\n\nI have already done objects.format here, which follows the same\nstructure.\n\n> Also, unless we capture both standard output (in actual) and\n> standard error (in actual_err) from the command being tested, and\n> instead are interested in testing only what comes out of the error\n> stream, it is more common to use the usual \"expect vs actual\".\n\nOk. Should I send a v11 or is it ok to keep it like this by now?\n\nThanks!\n\n"},{"id":"524274","messageId":"xmqq5xeoqti3.fsf@gitster.g","threadId":"63618","inReplyTo":"20250815135521.44044-6-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v10 5/5] repo: add the --format flag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-15T19:23:16Z","receivedAt":"2025-08-15T19:23:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> Add the --format flag to git-repo-info. By using this flag, the users\n> can choose the format for obtaining the data they requested.\n>\n> Given that this command can be used for generating input for other\n> applications and for being read by end users, it requires at least two\n> formats: one for being read by humans and other for being read by\n> machines. Some other Git commands also have two output formats, notably\n> git-config which was the inspiration for the two formats that were\n> chosen here:\n>\n> - keyvalue, where the retrieved data is printed one per line, using =\n>   for delimiting the key and the value. This is the default format,\n>   targeted for end users.\n> - nul, where the retrieved data is separated by null characters, using\n\nThese characters are commonly spelled \"NUL characters\".\n\n    $ git grep -i -e 'NUL ch' -e 'NULL ch' Documentation/\n\nSame for the explanation for the \"nul\" in the documentation.\n\n> +\n> +\t\tswitch (format) {\n> +\t\tcase FORMAT_KEYVALUE:\n> +\t\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n> +\t\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n> +\t\t\tbreak;\n> +\t\tcase FORMAT_NUL_TERMINATED:\n> +\t\t\tprintf(\"%s\\n%s%c\", key, valbuf.buf, '\\0');\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tBUG(\"not a valid output format: %d\", format);\n> +\t\t}\n\nOK.\n\n> diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\n> index 78766a3f4f..a4ec6b42df 100755\n> --- a/t/t1900-repo.sh\n> +++ b/t/t1900-repo.sh\n> @@ -21,12 +21,22 @@ test_repo_info () {\n>  \tkey=$4\n>  \texpected_value=$5\n>  \n> -\ttest_expect_success \"$label\" '\n> -\t\teval \"$init_command $repo_name\" &&\n> -\t\techo \"$key=$expected_value\" >expected &&\n> -\t\tgit -C $repo_name repo info \"$key\" >actual &&\n> +\trepo_name_keyvalue=\"$repo_name\"-keyvalue\n> +\trepo_name_nul=\"$repo_name\"-nul\n> +\n> +\ttest_expect_success \"keyvalue: $label\" '\n> +\t\teval \"$init_command $repo_name_keyvalue\" &&\n> +\t\techo \"$key=$expected_value\" > expected &&\n> +\t\tgit -C \"$repo_name_keyvalue\" repo info \"$key\" >actual &&\n>  \t\ttest_cmp expected actual\n>  \t'\n> +\n> +\ttest_expect_success \"nul: $label\" '\n> +\t\teval \"$init_command $repo_name_nul\" &&\n> +\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n> +\t\tgit -C \"$repo_name_nul\" repo info --format=nul \"$key\" >actual &&\n> +\t\ttest_cmp_bin expected actual\n> +\t'\n>  }\n\nThis is curious.\n\nIf my understanding is correct, the --format=nul/keyvalue affects\nonly the output format when \"repo info\" is executed.  I do not see\nwhy we need two separate repositories.\n"},{"id":"524275","messageId":"xmqqzfc0peaw.fsf@gitster.g","threadId":"63618","inReplyTo":"20250815135521.44044-5-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v10 4/5] repo: add the field layout.shallow","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-15T19:36:55Z","receivedAt":"2025-08-15T19:36:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n> @@ -41,6 +41,9 @@ values that they return:\n>  `layout.bare`::\n>  `true` if this is a bare repository, otherwise `false`.\n>  \n> +`layout.shallow`::\n> +`true` if this is a shallow repository, otherwise `false`.\n> +\n>  `references.format`::\n>  The reference storage format. The valid values are:\n>  +\n\nCommon to all three patches that adds each of these three, but I\nthink we want to see these definition list like so:\n\n\t`layout.bare`::\n\t\t`true` if this is a bare...\n\n\t`layout.shallow`::\n\t\t'true` if this is a shallow...\n\n\t`reference.format`::\n\t\tThe reference storage format.\n\nIt should not make any difference in the rendered result.  The\nexisting documentation pages follow this style because it makes the\nlayout of the text in the source file resemble the formatted output\nmore closely.\n\nAlso, I wonder if it makes the document structure (from the point of\nview of mark-up) more apparent if we make the listing of two\npossible values for the \"--format=\" option a nested definition list,\ninside the definition list with a single element \"info\" in it.\n\nSomething like the following on top of your series, that is.\n\nThe first hunk does make the result rendered differently; the second\nhunk should produce the identical rendition as your version.\n\nThanks.\n\n Documentation/git-repo.adoc | 24 +++++++++++++-----------\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git c/Documentation/git-repo.adoc w/Documentation/git-repo.adoc\nindex cb22f7ef65..5424deea69 100644\n--- c/Documentation/git-repo.adoc\n+++ w/Documentation/git-repo.adoc\n@@ -29,15 +29,17 @@ requested.\n The output format can be chosen through the flag `--format`. Two formats are\n supported:\n +\n-* `keyvalue`: output key-value pairs one per line using the `=` character as\n-the delimiter between the key and the value. Values containing \"unusual\"\n-characters are quoted as explained for the configuration variable\n-`core.quotePath` (see linkgit:git-config[1]). This is the default.\n+`keyvalue`;;\n+\toutput key-value pairs one per line using the `=` character as\n+\tthe delimiter between the key and the value. Values containing \"unusual\"\n+\tcharacters are quoted as explained for the configuration variable\n+\t`core.quotePath` (see linkgit:git-config[1]). This is the default.\n \n-* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n-between the key and the value and using a null character after each value.\n-This format is better suited for being parsed by another applications than\n-`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n+`nul`;;\n+\tsimilar to `keyvalue`, but using a newline character as the delimiter\n+\tbetween the key and the value and using a null character after each value.\n+\tThis format is better suited for being parsed by another applications than\n+\t`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n \n INFO KEYS\n ---------\n@@ -46,13 +48,13 @@ the keys that identify them. Here's a list of the available keys and the\n values that they return:\n \n `layout.bare`::\n-`true` if this is a bare repository, otherwise `false`.\n+\t`true` if this is a bare repository, otherwise `false`.\n \n `layout.shallow`::\n-`true` if this is a shallow repository, otherwise `false`.\n+\t`true` if this is a shallow repository, otherwise `false`.\n \n `references.format`::\n-The reference storage format. The valid values are:\n+\tThe reference storage format. The valid values are:\n +\n include::ref-storage-format.adoc[]\n \n"},{"id":"524307","messageId":"20250816224603.3307-1-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250610152117.14826-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v11 0/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-16T22:45:58Z","receivedAt":"2025-08-16T22:46:30Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Hi!\n\nThis v11 addresses the minor issues pointed by Junio in v10. They are mostly\nspelling changes:\n\n- using \"expect\" and \"actual\" in all test_cmp calls, even when we're capturing\n  stderr\n\n- indent list items in the documentation\n\n- use a nested description list when describing the possible values of --format\n\n- spelling: \"NUL characters\" instead of \"null characters\"\n\n- initialize the repository in `test_repo_info` outside the test cases,\n  creating a single repository for both keyvalue and nul formats\n\nHere's the rangediff versus v10:\n\n1:  6767028da3 = 1:  6767028da3 repo: declare the repo command\n2:  c44e2d1619 ! 2:  1a3fa8e12f repo: add the field references.format\n    @@ Documentation/git-repo.adoc: COMMANDS\n     +values that they return:\n     +\n     +`references.format`::\n    -+The reference storage format. The valid values are:\n    ++\tThe reference storage format. The valid values are:\n     ++\n     +include::ref-storage-format.adoc[]\n\n    @@ t/t1900-repo.sh (new)\n     +\tkey=$4\n     +\texpected_value=$5\n     +\n    ++\ttest_expect_success \"setup: $label\" '\n    ++\t\teval \"$init_command $repo_name\"\n    ++\t'\n    ++\n     +\ttest_expect_success \"$label\" '\n    -+\t\teval \"$init_command $repo_name\" &&\n    -+\t\techo \"$key=$expected_value\" >expected &&\n    ++\t\techo \"$key=$expected_value\" >expect &&\n     +\t\tgit -C $repo_name repo info \"$key\" >actual &&\n    -+\t\ttest_cmp expected actual\n    ++\t\ttest_cmp expect actual\n     +\t'\n     +}\n     +\n    @@ t/t1900-repo.sh (new)\n     +\t'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n     +\n     +test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    -+\techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n    -+\ttest_must_fail git repo info foo 2>actual_err &&\n    -+\ttest_cmp expected_err actual_err\n    ++\techo \"error: key ${SQ}foo${SQ} not found\" >expect &&\n    ++\ttest_must_fail git repo info foo 2>actual &&\n    ++\ttest_cmp expect actual\n     +'\n     +\n     +test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n    -+\techo \"references.format=$(test_detect_ref_format)\" >expected &&\n    ++\techo \"references.format=$(test_detect_ref_format)\" >expect &&\n     +\ttest_must_fail git repo info foo references.format bar >actual &&\n    -+\ttest_cmp expected actual\n    ++\ttest_cmp expect actual\n     +'\n     +\n     +test_done\n3:  e3009a85e1 ! 3:  a3b05def63 repo: add the field layout.bare\n    @@ Documentation/git-repo.adoc: In order to obtain a set of values from `git repo i\n      values that they return:\n\n     +`layout.bare`::\n    -+`true` if this is a bare repository, otherwise `false`.\n    ++\t`true` if this is a bare repository, otherwise `false`.\n     +\n      `references.format`::\n    - The reference storage format. The valid values are:\n    + \tThe reference storage format. The valid values are:\n      +\n\n      ## builtin/repo.c ##\n    @@ t/t1900-repo.sh: test_repo_info 'ref format files is retrieved correctly' \\\n     +'\n     +\n      test_expect_success 'git-repo-info fails if an invalid key is requested' '\n    - \techo \"error: key ${SQ}foo${SQ} not found\" >expected_err &&\n    - \ttest_must_fail git repo info foo 2>actual_err &&\n    + \techo \"error: key ${SQ}foo${SQ} not found\" >expect &&\n    + \ttest_must_fail git repo info foo 2>actual &&\n4:  3837899c32 ! 4:  034dd414e5 repo: add the field layout.shallow\n    @@ Commit message\n      ## Documentation/git-repo.adoc ##\n     @@ Documentation/git-repo.adoc: values that they return:\n      `layout.bare`::\n    - `true` if this is a bare repository, otherwise `false`.\n    + \t`true` if this is a bare repository, otherwise `false`.\n\n     +`layout.shallow`::\n    -+`true` if this is a shallow repository, otherwise `false`.\n    ++\t`true` if this is a shallow repository, otherwise `false`.\n     +\n      `references.format`::\n    - The reference storage format. The valid values are:\n    + \tThe reference storage format. The valid values are:\n      +\n\n      ## builtin/repo.c ##\n5:  19fdfce646 ! 5:  442a3b7afa repo: add the --format flag\n    @@ Commit message\n         - keyvalue, where the retrieved data is printed one per line, using =\n           for delimiting the key and the value. This is the default format,\n           targeted for end users.\n    -    - nul, where the retrieved data is separated by null characters, using\n    +    - nul, where the retrieved data is separated by NUL characters, using\n           the newline character for delimiting the key and the value. This\n           format is targeted for being read by machines.\n\n    @@ Documentation/git-repo.adoc: COMMANDS\n     +The output format can be chosen through the flag `--format`. Two formats are\n     +supported:\n     ++\n    -+* `keyvalue`: output key-value pairs one per line using the `=` character as\n    -+the delimiter between the key and the value. Values containing \"unusual\"\n    -+characters are quoted as explained for the configuration variable\n    -+`core.quotePath` (see linkgit:git-config[1]). This is the default.\n    ++`keyvalue`:::\n    ++\toutput key-value pairs one per line using the `=` character as\n    ++\tthe delimiter between the key and the value. Values containing \"unusual\"\n    ++\tcharacters are quoted as explained for the configuration variable\n    ++\t`core.quotePath` (see linkgit:git-config[1]). This is the default.\n     +\n    -+* `nul`: similar to `keyvalue`, but using a newline character as the delimiter\n    -+between the key and the value and using a null character after each value.\n    -+This format is better suited for being parsed by another applications than\n    -+`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n    ++`nul`:::\n    ++\tsimilar to `keyvalue`, but using a newline character as the delimiter\n    ++\tbetween the key and the value and using a NUL character after each value.\n    ++\tThis format is better suited for being parsed by another applications than\n    ++\t`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n\n      INFO KEYS\n      ---------\n    @@ Documentation/git-repo.adoc: COMMANDS\n      In order to obtain a set of values from `git repo info`, you should provide\n      the keys that identify them. Here's a list of the available keys and the\n      values that they return:\n    -@@ Documentation/git-repo.adoc: The reference storage format. The valid values are:\n    +@@ Documentation/git-repo.adoc: values that they return:\n      +\n      include::ref-storage-format.adoc[]\n\n    @@ builtin/repo.c: static int print_fields(int argc, const char **argv, struct repo\n\n      ## t/t1900-repo.sh ##\n     @@ t/t1900-repo.sh: test_repo_info () {\n    - \tkey=$4\n    - \texpected_value=$5\n    + \t\teval \"$init_command $repo_name\"\n    + \t'\n\n     -\ttest_expect_success \"$label\" '\n    --\t\teval \"$init_command $repo_name\" &&\n    --\t\techo \"$key=$expected_value\" >expected &&\n    +-\t\techo \"$key=$expected_value\" >expect &&\n     -\t\tgit -C $repo_name repo info \"$key\" >actual &&\n    -+\trepo_name_keyvalue=\"$repo_name\"-keyvalue\n    -+\trepo_name_nul=\"$repo_name\"-nul\n    -+\n     +\ttest_expect_success \"keyvalue: $label\" '\n    -+\t\teval \"$init_command $repo_name_keyvalue\" &&\n    -+\t\techo \"$key=$expected_value\" > expected &&\n    -+\t\tgit -C \"$repo_name_keyvalue\" repo info \"$key\" >actual &&\n    - \t\ttest_cmp expected actual\n    ++\t\techo \"$key=$expected_value\" > expect &&\n    ++\t\tgit -C \"$repo_name\" repo info \"$key\" >actual &&\n    + \t\ttest_cmp expect actual\n      \t'\n     +\n     +\ttest_expect_success \"nul: $label\" '\n    -+\t\teval \"$init_command $repo_name_nul\" &&\n    -+\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expected &&\n    -+\t\tgit -C \"$repo_name_nul\" repo info --format=nul \"$key\" >actual &&\n    -+\t\ttest_cmp_bin expected actual\n    ++\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expect &&\n    ++\t\tgit -C \"$repo_name\" repo info --format=nul \"$key\" >actual &&\n    ++\t\ttest_cmp_bin expect actual\n     +\t'\n      }\n\n      test_repo_info 'ref format files is retrieved correctly' \\\n     @@ t/t1900-repo.sh: test_expect_success 'git-repo-info outputs data even if there is an invalid fiel\n    - \ttest_cmp expected actual\n    + \ttest_cmp expect actual\n      '\n\n     +test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n    -+\techo \"fatal: invalid format ${SQ}foo${SQ}\" >expected &&\n    -+\ttest_must_fail git repo info --format=foo 2>err &&\n    -+\ttest_cmp expected err\n    ++\techo \"fatal: invalid format ${SQ}foo${SQ}\" >expect &&\n    ++\ttest_must_fail git repo info --format=foo 2>actual &&\n    ++\ttest_cmp expect actual\n     +'\n     +\n      test_done\n\nLucas Seiki Oshiro (5):\n  repo: declare the repo command\n  repo: add the field references.format\n  repo: add the field layout.bare\n  repo: add the field layout.shallow\n  repo: add the --format flag\n\n .gitignore                  |   1 +\n Documentation/git-repo.adoc |  84 ++++++++++++++++++++\n Documentation/meson.build   |   1 +\n Makefile                    |   1 +\n builtin.h                   |   1 +\n builtin/repo.c              | 150 ++++++++++++++++++++++++++++++++++++\n command-list.txt            |   1 +\n git.c                       |   1 +\n meson.build                 |   1 +\n t/meson.build               |   1 +\n t/t1900-repo.sh             |  95 +++++++++++++++++++++++\n 11 files changed, 337 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n create mode 100755 t/t1900-repo.sh\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524308","messageId":"20250816224603.3307-2-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250816224603.3307-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v11 1/5] repo: declare the repo command","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-16T22:45:59Z","receivedAt":"2025-08-16T22:46:34Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Currently, `git rev-parse` covers a wide range of functionality not\ndirectly related to parsing revisions, as its name suggests. Over time,\nmany features like parsing datestrings, options, paths, and others\nwere added to it because there wasn't a more appropriate command\nto place them.\n\nCreate a new Git command called `repo`. `git repo` will be the main\ncommand for obtaining the information about a repository (such as\nmetadata and metrics).\n\nAlso declare a subcommand for `repo` called `info`. `git repo info`\nwill bring the functionality of retrieving repository-related\ninformation currently returned by `rev-parse`.\n\nAdd the required documentation and build changes to enable usage of\nthis subcommand.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n .gitignore                  |  1 +\n Documentation/git-repo.adoc | 32 ++++++++++++++++++++++++++++++++\n Documentation/meson.build   |  1 +\n Makefile                    |  1 +\n builtin.h                   |  1 +\n builtin/repo.c              | 27 +++++++++++++++++++++++++++\n command-list.txt            |  1 +\n git.c                       |  1 +\n meson.build                 |  1 +\n 9 files changed, 66 insertions(+)\n create mode 100644 Documentation/git-repo.adoc\n create mode 100644 builtin/repo.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 04c444404e..1803023427 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -139,6 +139,7 @@\n /git-repack\n /git-replace\n /git-replay\n+/git-repo\n /git-request-pull\n /git-rerere\n /git-reset\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nnew file mode 100644\nindex 0000000000..68c706f5a0\n--- /dev/null\n+++ b/Documentation/git-repo.adoc\n@@ -0,0 +1,32 @@\n+git-repo(1)\n+===========\n+\n+NAME\n+----\n+git-repo - Retrieve information about the repository\n+\n+SYNOPSIS\n+--------\n+[synopsis]\n+git repo info [<key>...]\n+\n+DESCRIPTION\n+-----------\n+Retrieve information about the repository.\n+\n+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n+\n+COMMANDS\n+--------\n+`info [<key>...]`::\n+\tRetrieve metadata-related information about the current repository. Only\n+\tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n+\tsection below).\n+\n+SEE ALSO\n+--------\n+linkgit:git-rev-parse[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 4404c623f0..41f43e0336 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -116,6 +116,7 @@ manpages = {\n   'git-repack.adoc' : 1,\n   'git-replace.adoc' : 1,\n   'git-replay.adoc' : 1,\n+  'git-repo.adoc' : 1,\n   'git-request-pull.adoc' : 1,\n   'git-rerere.adoc' : 1,\n   'git-reset.adoc' : 1,\ndiff --git a/Makefile b/Makefile\nindex e11340c1ae..ec7ac58980 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1306,6 +1306,7 @@ BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/replay.o\n+BUILTIN_OBJS += builtin/repo.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex bff13e3069..e6458e6fb9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -216,6 +216,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos\n int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo);\n+int cmd_repo(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo);\n int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo);\ndiff --git a/builtin/repo.c b/builtin/repo.c\nnew file mode 100644\nindex 0000000000..fd2a9b4216\n--- /dev/null\n+++ b/builtin/repo.c\n@@ -0,0 +1,27 @@\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+\n+static const char *const repo_usage[] = {\n+\t\"git repo info [<key>...]\",\n+\tNULL\n+};\n+\n+static int repo_info(int argc UNUSED, const char **argv UNUSED,\n+\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+{\n+\treturn 0;\n+}\n+\n+int cmd_repo(int argc, const char **argv, const char *prefix,\n+\t     struct repository *repo)\n+{\n+\tparse_opt_subcommand_fn *fn = NULL;\n+\tstruct option options[] = {\n+\t\tOPT_SUBCOMMAND(\"info\", &fn, repo_info),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\treturn fn(argc, argv, prefix, repo);\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex b7ade3ab9f..1b0bdee00d 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -164,6 +164,7 @@ git-remote                              ancillarymanipulators           complete\n git-repack                              ancillarymanipulators           complete\n git-replace                             ancillarymanipulators           complete\n git-replay                              plumbingmanipulators\n+git-repo                                plumbinginterrogators\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           history\ndiff --git a/git.c b/git.c\nindex 83eac0aeab..d4ff4d5517 100644\n--- a/git.c\n+++ b/git.c\n@@ -611,6 +611,7 @@ static struct cmd_struct commands[] = {\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"replay\", cmd_replay, RUN_SETUP },\n+\t{ \"repo\", cmd_repo, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/meson.build b/meson.build\nindex 5dd299b496..e8ec0eca16 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -645,6 +645,7 @@ builtin_sources = [\n   'builtin/repack.c',\n   'builtin/replace.c',\n   'builtin/replay.c',\n+  'builtin/repo.c',\n   'builtin/rerere.c',\n   'builtin/reset.c',\n   'builtin/rev-list.c',\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524309","messageId":"20250816224603.3307-3-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250816224603.3307-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v11 2/5] repo: add the field references.format","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-16T22:46:00Z","receivedAt":"2025-08-16T22:46:38Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--show-ref-format` from git-rev-parse is used for retrieving\nthe reference format (i.e. `files` or `reftable`). This way, it is\nused for querying repository metadata, fitting in the purpose of\ngit-repo-info.\n\nAdd a new field `references.format` to the repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 20 ++++++++++\n builtin/repo.c              | 74 ++++++++++++++++++++++++++++++++++++-\n t/meson.build               |  1 +\n t/t1900-repo.sh             | 53 ++++++++++++++++++++++++++\n 4 files changed, 146 insertions(+), 2 deletions(-)\n create mode 100755 t/t1900-repo.sh\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 68c706f5a0..2779a6d995 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -22,6 +22,26 @@ COMMANDS\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n++\n+The values are returned in the same order in which their respective keys were\n+requested.\n++\n+The output format consists of key-value pairs one per line using the `=`\n+character as the delimiter between the key and the value. Values containing\n+\"unusual\" characters are quoted as explained for the configuration variable\n+`core.quotePath` (see linkgit:git-config[1]).\n+\n+INFO KEYS\n+---------\n+\n+In order to obtain a set of values from `git repo info`, you should provide\n+the keys that identify them. Here's a list of the available keys and the\n+values that they return:\n+\n+`references.format`::\n+\tThe reference storage format. The valid values are:\n++\n+include::ref-storage-format.adoc[]\n \n SEE ALSO\n --------\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex fd2a9b4216..73d4e27a16 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,17 +1,87 @@\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"quote.h\"\n+#include \"refs.h\"\n+#include \"strbuf.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n \tNULL\n };\n \n-static int repo_info(int argc UNUSED, const char **argv UNUSED,\n-\t\t     const char *prefix UNUSED, struct repository *repo UNUSED)\n+typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n+\n+struct field {\n+\tconst char *key;\n+\tget_value_fn *get_value;\n+};\n+\n+static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n+\tstrbuf_addstr(buf,\n+\t\t      ref_storage_format_to_name(repo->ref_storage_format));\n \treturn 0;\n }\n \n+/* repo_info_fields keys must be in lexicographical order */\n+static const struct field repo_info_fields[] = {\n+\t{ \"references.format\", get_references_format },\n+};\n+\n+static int repo_info_fields_cmp(const void *va, const void *vb)\n+{\n+\tconst struct field *a = va;\n+\tconst struct field *b = vb;\n+\n+\treturn strcmp(a->key, b->key);\n+}\n+\n+static get_value_fn *get_value_fn_for_key(const char *key)\n+{\n+\tconst struct field search_key = { key, NULL };\n+\tconst struct field *found = bsearch(&search_key, repo_info_fields,\n+\t\t\t\t\t    ARRAY_SIZE(repo_info_fields),\n+\t\t\t\t\t    sizeof(*found),\n+\t\t\t\t\t    repo_info_fields_cmp);\n+\treturn found ? found->get_value : NULL;\n+}\n+\n+static int print_fields(int argc, const char **argv, struct repository *repo)\n+{\n+\tint ret = 0;\n+\tstruct strbuf valbuf = STRBUF_INIT;\n+\tstruct strbuf quotbuf = STRBUF_INIT;\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tget_value_fn *get_value;\n+\t\tconst char *key = argv[i];\n+\n+\t\tget_value = get_value_fn_for_key(key);\n+\n+\t\tif (!get_value) {\n+\t\t\tret = error(_(\"key '%s' not found\"), key);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tstrbuf_reset(&valbuf);\n+\t\tstrbuf_reset(&quotbuf);\n+\n+\t\tget_value(repo, &valbuf);\n+\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n+\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\t}\n+\n+\tstrbuf_release(&valbuf);\n+\tstrbuf_release(&quotbuf);\n+\treturn ret;\n+}\n+\n+static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+\t\t     struct repository *repo)\n+{\n+\treturn print_fields(argc - 1, argv + 1, repo);\n+}\n+\n int cmd_repo(int argc, const char **argv, const char *prefix,\n \t     struct repository *repo)\n {\ndiff --git a/t/meson.build b/t/meson.build\nindex 983245501c..7555d52917 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -231,6 +231,7 @@ integration_tests = [\n   't1700-split-index.sh',\n   't1701-racy-split-index.sh',\n   't1800-hook.sh',\n+  't1900-repo.sh',\n   't2000-conflict-when-checking-files-out.sh',\n   't2002-checkout-cache-u.sh',\n   't2003-checkout-cache-mkdir.sh',\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nnew file mode 100755\nindex 0000000000..be8a4b2499\n--- /dev/null\n+++ b/t/t1900-repo.sh\n@@ -0,0 +1,53 @@\n+#!/bin/sh\n+\n+test_description='test git repo-info'\n+\n+. ./test-lib.sh\n+\n+# Test whether a key-value pair is correctly returned\n+#\n+# Usage: test_repo_info <label> <init command> <repo_name> <key> <expected value>\n+#\n+# Arguments:\n+#   label: the label of the test\n+#   init_command: a command which creates a repository\n+#   repo_name: the name of the repository that will be created in init_command\n+#   key: the key of the field that is being tested\n+#   expected_value: the value that the field should contain\n+test_repo_info () {\n+\tlabel=$1\n+\tinit_command=$2\n+\trepo_name=$3\n+\tkey=$4\n+\texpected_value=$5\n+\n+\ttest_expect_success \"setup: $label\" '\n+\t\teval \"$init_command $repo_name\"\n+\t'\n+\n+\ttest_expect_success \"$label\" '\n+\t\techo \"$key=$expected_value\" >expect &&\n+\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_repo_info 'ref format files is retrieved correctly' \\\n+\t'git init --ref-format=files' 'format-files' 'references.format' 'files'\n+\n+test_repo_info 'ref format reftable is retrieved correctly' \\\n+\t'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n+\n+test_expect_success 'git-repo-info fails if an invalid key is requested' '\n+\techo \"error: key ${SQ}foo${SQ} not found\" >expect &&\n+\ttest_must_fail git repo info foo 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'git-repo-info outputs data even if there is an invalid field' '\n+\techo \"references.format=$(test_detect_ref_format)\" >expect &&\n+\ttest_must_fail git repo info foo references.format bar >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524310","messageId":"20250816224603.3307-4-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250816224603.3307-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v11 3/5] repo: add the field layout.bare","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-16T22:46:01Z","receivedAt":"2025-08-16T22:46:42Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag --is-bare-repository from git-rev-parse is used for retrieving\nwhether the current repository is bare. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field layout.bare to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              | 10 ++++++++++\n t/t1900-repo.sh             | 17 +++++++++++++++++\n 3 files changed, 30 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 2779a6d995..932b08c26f 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -38,6 +38,9 @@ In order to obtain a set of values from `git repo info`, you should provide\n the keys that identify them. Here's a list of the available keys and the\n values that they return:\n \n+`layout.bare`::\n+\t`true` if this is a bare repository, otherwise `false`.\n+\n `references.format`::\n \tThe reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 73d4e27a16..aada476e1c 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -1,4 +1,7 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n #include \"builtin.h\"\n+#include \"environment.h\"\n #include \"parse-options.h\"\n #include \"quote.h\"\n #include \"refs.h\"\n@@ -16,6 +19,12 @@ struct field {\n \tget_value_fn *get_value;\n };\n \n+static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf, is_bare_repository() ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -25,6 +34,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n \n /* repo_info_fields keys must be in lexicographical order */\n static const struct field repo_info_fields[] = {\n+\t{ \"layout.bare\", get_layout_bare },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex be8a4b2499..b0438d276e 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -38,6 +38,23 @@ test_repo_info 'ref format files is retrieved correctly' \\\n test_repo_info 'ref format reftable is retrieved correctly' \\\n \t'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable'\n \n+test_repo_info 'bare repository = false is retrieved correctly' \\\n+\t'git init' 'nonbare' 'layout.bare' 'false'\n+\n+test_repo_info 'bare repository = true is retrieved correctly' \\\n+\t'git init --bare' 'bare' 'layout.bare' 'true'\n+\n+test_expect_success 'values returned in order requested' '\n+\tcat >expect <<-\\EOF &&\n+\tlayout.bare=false\n+\treferences.format=files\n+\tlayout.bare=false\n+\tEOF\n+\tgit init --ref-format=files ordered &&\n+\tgit -C ordered repo info layout.bare references.format layout.bare >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'git-repo-info fails if an invalid key is requested' '\n \techo \"error: key ${SQ}foo${SQ} not found\" >expect &&\n \ttest_must_fail git repo info foo 2>actual &&\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524311","messageId":"20250816224603.3307-5-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250816224603.3307-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v11 4/5] repo: add the field layout.shallow","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-16T22:46:02Z","receivedAt":"2025-08-16T22:46:45Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"This commit is part of the series that introduces the new subcommand\ngit-repo-info.\n\nThe flag `--is-shallow-repository` from git-rev-parse is used for\nretrieving whether the repository is shallow. This way, it is used for\nquerying repository metadata, fitting in the purpose of git-repo-info.\n\nThen, add a new field `layout.shallow` to the git-repo-info subcommand\ncontaining that information.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc |  3 +++\n builtin/repo.c              |  9 +++++++++\n t/t1900-repo.sh             | 13 +++++++++++++\n 3 files changed, 25 insertions(+)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 932b08c26f..01b7f9c95e 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -41,6 +41,9 @@ values that they return:\n `layout.bare`::\n \t`true` if this is a bare repository, otherwise `false`.\n \n+`layout.shallow`::\n+\t`true` if this is a shallow repository, otherwise `false`.\n+\n `references.format`::\n \tThe reference storage format. The valid values are:\n +\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex aada476e1c..3c9140593b 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -6,6 +6,7 @@\n #include \"quote.h\"\n #include \"refs.h\"\n #include \"strbuf.h\"\n+#include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n \t\"git repo info [<key>...]\",\n@@ -25,6 +26,13 @@ static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf)\n \treturn 0;\n }\n \n+static int get_layout_shallow(struct repository *repo, struct strbuf *buf)\n+{\n+\tstrbuf_addstr(buf,\n+\t\t      is_repository_shallow(repo) ? \"true\" : \"false\");\n+\treturn 0;\n+}\n+\n static int get_references_format(struct repository *repo, struct strbuf *buf)\n {\n \tstrbuf_addstr(buf,\n@@ -35,6 +43,7 @@ static int get_references_format(struct repository *repo, struct strbuf *buf)\n /* repo_info_fields keys must be in lexicographical order */\n static const struct field repo_info_fields[] = {\n \t{ \"layout.bare\", get_layout_bare },\n+\t{ \"layout.shallow\", get_layout_shallow },\n \t{ \"references.format\", get_references_format },\n };\n \ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex b0438d276e..6a9cbf3d47 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -44,6 +44,19 @@ test_repo_info 'bare repository = false is retrieved correctly' \\\n test_repo_info 'bare repository = true is retrieved correctly' \\\n \t'git init --bare' 'bare' 'layout.bare' 'true'\n \n+test_repo_info 'shallow repository = false is retrieved correctly' \\\n+\t'git init' 'nonshallow' 'layout.shallow' 'false'\n+\n+test_expect_success 'setup remote' '\n+\tgit init remote &&\n+\techo x >remote/x &&\n+\tgit -C remote add x &&\n+\tgit -C remote commit -m x\n+'\n+\n+test_repo_info 'shallow repository = true is retrieved correctly' \\\n+\t'git clone --depth 1 \"file://$PWD/remote\"' 'shallow' 'layout.shallow' 'true'\n+\n test_expect_success 'values returned in order requested' '\n \tcat >expect <<-\\EOF &&\n \tlayout.bare=false\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524312","messageId":"20250816224603.3307-6-lucasseikioshiro@gmail.com","threadId":"63618","inReplyTo":"20250816224603.3307-1-lucasseikioshiro@gmail.com","subject":"[GSoC PATCH v11 5/5] repo: add the --format flag","fromName":"Lucas Seiki Oshiro","fromEmail":"lucasseikioshiro@gmail.com","sentAt":"2025-08-16T22:46:03Z","receivedAt":"2025-08-16T22:46:49Z","isPatch":true,"sender":{"key":"lucasseikioshiro@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701580?v=4"},"body":"Add the --format flag to git-repo-info. By using this flag, the users\ncan choose the format for obtaining the data they requested.\n\nGiven that this command can be used for generating input for other\napplications and for being read by end users, it requires at least two\nformats: one for being read by humans and other for being read by\nmachines. Some other Git commands also have two output formats, notably\ngit-config which was the inspiration for the two formats that were\nchosen here:\n\n- keyvalue, where the retrieved data is printed one per line, using =\n  for delimiting the key and the value. This is the default format,\n  targeted for end users.\n- nul, where the retrieved data is separated by NUL characters, using\n  the newline character for delimiting the key and the value. This\n  format is targeted for being read by machines.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Justin Tobler <jltobler@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nMentored-by: Karthik Nayak <karthik.188@gmail.com>\nMentored-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>\n---\n Documentation/git-repo.adoc | 40 ++++++++++++++++++++++++++------\n builtin/repo.c              | 46 ++++++++++++++++++++++++++++++++-----\n t/t1900-repo.sh             | 18 ++++++++++++---\n 3 files changed, 88 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc\nindex 01b7f9c95e..2870828d93 100644\n--- a/Documentation/git-repo.adoc\n+++ b/Documentation/git-repo.adoc\n@@ -8,7 +8,7 @@ git-repo - Retrieve information about the repository\n SYNOPSIS\n --------\n [synopsis]\n-git repo info [<key>...]\n+git repo info [--format=(keyvalue|nul)] [<key>...]\n \n DESCRIPTION\n -----------\n@@ -18,7 +18,7 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.\n \n COMMANDS\n --------\n-`info [<key>...]`::\n+`info [--format=(keyvalue|nul)] [<key>...]`::\n \tRetrieve metadata-related information about the current repository. Only\n \tthe requested data will be returned based on their keys (see \"INFO KEYS\"\n \tsection below).\n@@ -26,14 +26,23 @@ COMMANDS\n The values are returned in the same order in which their respective keys were\n requested.\n +\n-The output format consists of key-value pairs one per line using the `=`\n-character as the delimiter between the key and the value. Values containing\n-\"unusual\" characters are quoted as explained for the configuration variable\n-`core.quotePath` (see linkgit:git-config[1]).\n+The output format can be chosen through the flag `--format`. Two formats are\n+supported:\n++\n+`keyvalue`:::\n+\toutput key-value pairs one per line using the `=` character as\n+\tthe delimiter between the key and the value. Values containing \"unusual\"\n+\tcharacters are quoted as explained for the configuration variable\n+\t`core.quotePath` (see linkgit:git-config[1]). This is the default.\n+\n+`nul`:::\n+\tsimilar to `keyvalue`, but using a newline character as the delimiter\n+\tbetween the key and the value and using a NUL character after each value.\n+\tThis format is better suited for being parsed by another applications than\n+\t`keyvalue`. Unlike in the `keyvalue` format, the values are never quoted.\n \n INFO KEYS\n ---------\n-\n In order to obtain a set of values from `git repo info`, you should provide\n the keys that identify them. Here's a list of the available keys and the\n values that they return:\n@@ -49,6 +58,23 @@ values that they return:\n +\n include::ref-storage-format.adoc[]\n \n+EXAMPLES\n+--------\n+\n+* Retrieves the reference format of the current repository:\n++\n+------------\n+git repo info references.format\n+------------\n++\n+\n+* Retrieves whether the current repository is bare and whether it is shallow\n+using the `nul` format:\n++\n+------------\n+git repo info --format=nul layout.bare layout.shallow\n+------------\n+\n SEE ALSO\n --------\n linkgit:git-rev-parse[1]\ndiff --git a/builtin/repo.c b/builtin/repo.c\nindex 3c9140593b..8c6e7f42ab 100644\n--- a/builtin/repo.c\n+++ b/builtin/repo.c\n@@ -9,12 +9,17 @@\n #include \"shallow.h\"\n \n static const char *const repo_usage[] = {\n-\t\"git repo info [<key>...]\",\n+\t\"git repo info [--format=(keyvalue|nul)] [<key>...]\",\n \tNULL\n };\n \n typedef int get_value_fn(struct repository *repo, struct strbuf *buf);\n \n+enum output_format {\n+\tFORMAT_KEYVALUE,\n+\tFORMAT_NUL_TERMINATED,\n+};\n+\n struct field {\n \tconst char *key;\n \tget_value_fn *get_value;\n@@ -65,7 +70,9 @@ static get_value_fn *get_value_fn_for_key(const char *key)\n \treturn found ? found->get_value : NULL;\n }\n \n-static int print_fields(int argc, const char **argv, struct repository *repo)\n+static int print_fields(int argc, const char **argv,\n+\t\t\tstruct repository *repo,\n+\t\t\tenum output_format format)\n {\n \tint ret = 0;\n \tstruct strbuf valbuf = STRBUF_INIT;\n@@ -86,8 +93,18 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \t\tstrbuf_reset(&quotbuf);\n \n \t\tget_value(repo, &valbuf);\n-\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n-\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\n+\t\tswitch (format) {\n+\t\tcase FORMAT_KEYVALUE:\n+\t\t\tquote_c_style(valbuf.buf, &quotbuf, NULL, 0);\n+\t\t\tprintf(\"%s=%s\\n\", key, quotbuf.buf);\n+\t\t\tbreak;\n+\t\tcase FORMAT_NUL_TERMINATED:\n+\t\t\tprintf(\"%s\\n%s%c\", key, valbuf.buf, '\\0');\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"not a valid output format: %d\", format);\n+\t\t}\n \t}\n \n \tstrbuf_release(&valbuf);\n@@ -95,10 +112,27 @@ static int print_fields(int argc, const char **argv, struct repository *repo)\n \treturn ret;\n }\n \n-static int repo_info(int argc, const char **argv, const char *prefix UNUSED,\n+static int repo_info(int argc, const char **argv, const char *prefix,\n \t\t     struct repository *repo)\n {\n-\treturn print_fields(argc - 1, argv + 1, repo);\n+\tconst char *format_str = \"keyvalue\";\n+\tenum output_format format;\n+\tstruct option options[] = {\n+\t\tOPT_STRING(0, \"format\", &format_str, N_(\"format\"),\n+\t\t\t   N_(\"output format\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, repo_usage, 0);\n+\n+\tif (!strcmp(format_str, \"keyvalue\"))\n+\t\tformat = FORMAT_KEYVALUE;\n+\telse if (!strcmp(format_str, \"nul\"))\n+\t\tformat = FORMAT_NUL_TERMINATED;\n+\telse\n+\t\tdie(_(\"invalid format '%s'\"), format_str);\n+\n+\treturn print_fields(argc, argv, repo, format);\n }\n \n int cmd_repo(int argc, const char **argv, const char *prefix,\ndiff --git a/t/t1900-repo.sh b/t/t1900-repo.sh\nindex 6a9cbf3d47..a69c715357 100755\n--- a/t/t1900-repo.sh\n+++ b/t/t1900-repo.sh\n@@ -25,11 +25,17 @@ test_repo_info () {\n \t\teval \"$init_command $repo_name\"\n \t'\n \n-\ttest_expect_success \"$label\" '\n-\t\techo \"$key=$expected_value\" >expect &&\n-\t\tgit -C $repo_name repo info \"$key\" >actual &&\n+\ttest_expect_success \"keyvalue: $label\" '\n+\t\techo \"$key=$expected_value\" > expect &&\n+\t\tgit -C \"$repo_name\" repo info \"$key\" >actual &&\n \t\ttest_cmp expect actual\n \t'\n+\n+\ttest_expect_success \"nul: $label\" '\n+\t\tprintf \"%s\\n%s\\0\" \"$key\" \"$expected_value\" >expect &&\n+\t\tgit -C \"$repo_name\" repo info --format=nul \"$key\" >actual &&\n+\t\ttest_cmp_bin expect actual\n+\t'\n }\n \n test_repo_info 'ref format files is retrieved correctly' \\\n@@ -80,4 +86,10 @@ test_expect_success 'git-repo-info outputs data even if there is an invalid fiel\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'git-repo-info aborts when requesting an invalid format' '\n+\techo \"fatal: invalid format ${SQ}foo${SQ}\" >expect &&\n+\ttest_must_fail git repo info --format=foo 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"524323","messageId":"xmqq7bz1ly0r.fsf@gitster.g","threadId":"63618","inReplyTo":"20250816224603.3307-1-lucasseikioshiro@gmail.com","subject":"Re: [GSoC PATCH v11 0/5] repo: declare the repo command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-17T16:21:24Z","receivedAt":"2025-08-17T16:21:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:\n\n>      +The output format can be chosen through the flag `--format`. Two formats are\n>      +supported:\n>      ++\n>     ++`keyvalue`:::\n>     ++\toutput key-value pairs one per line using the `=` character as\n\nEarlier I suggested to use ';;' out of habit, but using ':::' seems\nto be more kosher, according to\n\n    https://docs.asciidoctor.org/asciidoc/latest/lists/description/\n\nThanks for sticking to the best practice.\n\nLet's mark the topic for 'next', unless no further changes are\nneeded.\n\nThanks.\n"}]}