Subject: [GSoC RFC PATCH 3/5] repo-info: add the field references.format

From: Lucas Seiki Oshiro <[email-masked]>
Date: Tue, 10 Jun 2025 12:21:15 -0300

Add the field references.format to the repo-info command. The data
retrieved in this field is the same that currently is obtained by
running `git rev-parse --show-ref-format`.

Mentored-by: Karthik Nayak <[email-masked]>
Mentored-by Patrick Steinhardt <[email-masked]>
Signed-off-by: Lucas Seiki Oshiro <[email-masked]>
---
 builtin/repo-info.c  | 97 +++++++++++++++++++++++++++++++++++++++++---
 t/t1518-repo-info.sh | 20 +++++++++
 2 files changed, 112 insertions(+), 5 deletions(-)

diff --git a/builtin/repo-info.c b/builtin/repo-info.c
index 4d539a17fb..a1c9d3942e 100644
--- a/builtin/repo-info.c
+++ b/builtin/repo-info.c
@@ -9,18 +9,40 @@ enum output_format {
 	FORMAT_JSON
 };
 
+enum repo_info_category {
+	CATEGORY_REFERENCES = 1
+};
+
+enum repo_info_references_field {
+	FIELD_REFERENCES_FORMAT = 1
+};
+
+struct repo_info_field {
+	enum repo_info_category category;
+	union {
+		enum repo_info_references_field references;
+	} field;
+};
+
 struct repo_info {
 	struct repository *repo;
 	enum output_format format;
+	int n_fields;
+	struct repo_info_field *fields;
+};
+
+const char *default_fields[] = {
+	"references.format",
 };
 
 static void repo_info_init(struct repo_info *repo_info,
 			   struct repository *repo,
 			   char *format,
-			   int allow_empty UNUSED,
-			   int argc UNUSED,
-			   const char **argv UNUSED
+			   int allow_empty,
+			   int argc,
+			   const char **argv
 			   ) {
+	int i;
 	repo_info->repo = repo;
 
 	if (format == NULL || !strcmp(format, "json"))
@@ -29,18 +51,82 @@ static void repo_info_init(struct repo_info *repo_info,
 		repo_info->format = FORMAT_PLAINTEXT;
 	else
 		die("invalid format %s", format);
+
+	if (argc == 0 && !allow_empty) {
+		argc = ARRAY_SIZE(default_fields);
+		argv = default_fields;
+	}
+
+	repo_info->n_fields = argc;
+	repo_info->fields = xmalloc(argc * sizeof(struct repo_info_field));
+
+	for (i = 0; i < argc; i++) {
+		const char *arg = argv[i];
+		struct repo_info_field *field = repo_info->fields + i;
+
+		if (!strcmp(arg, "references.format")) {
+			field->category = CATEGORY_REFERENCES;
+			field->field.references = FIELD_REFERENCES_FORMAT;
+		}
+		else {
+			die("invalid field '%s'", arg);
+		}
+	}
+}
+
+static void repo_info_release(struct repo_info *repo_info) {
+	free(repo_info->fields);
 }
 
-static void repo_info_print_plaintext(struct repo_info *repo_info UNUSED) {
+static void repo_info_print_plaintext(struct repo_info *repo_info) {
+	struct repository *repo = repo_info->repo;
+	int i;
+	for (i = 0; i < repo_info->n_fields; i++) {
+		struct repo_info_field *field = &repo_info->fields[i];
+		switch (field->category) {
+		case CATEGORY_REFERENCES:
+			switch (field->field.references) {
+			case FIELD_REFERENCES_FORMAT:
+				puts(ref_storage_format_to_name(
+					repo->ref_storage_format));
+				break;
+			}
+			break;
+		}
+	}
 }
 
-static void repo_info_print_json(struct repo_info *repo_info UNUSED)
+static void repo_info_print_json(struct repo_info *repo_info)
 {
 	struct json_writer jw;
+	int i;
+	unsigned int categories = 0;
+	unsigned int references_fields = 0;
+	struct repository *repo = repo_info->repo;
+
+	for (i = 0; i < repo_info->n_fields; i++) {
+		struct repo_info_field *field = repo_info->fields + i;
+		categories |= field->category;
+		switch (field->category) {
+		case CATEGORY_REFERENCES:
+			references_fields |= field->field.references;
+			break;
+		}
+	}
 
 	jw_init(&jw);
 
 	jw_object_begin(&jw, 1);
+
+	if (categories & CATEGORY_REFERENCES) {
+		jw_object_inline_begin_object(&jw, "references");
+		if (references_fields & FIELD_REFERENCES_FORMAT) {
+			const char *format_name = ref_storage_format_to_name(
+				repo->ref_storage_format);
+			jw_object_string(&jw, "format", format_name);
+		}
+		jw_end(&jw);
+	}
 	jw_end(&jw);
 
 	puts(jw.json.buf);
@@ -92,6 +178,7 @@ int cmd_repo_info(
 			     PARSE_OPT_KEEP_UNKNOWN_OPT);
 	repo_info_init(&repo_info, repo, format, allow_empty, argc, argv);
 	repo_info_print(&repo_info);
+	repo_info_release(&repo_info);
 
 	return 0;
 }
diff --git a/t/t1518-repo-info.sh b/t/t1518-repo-info.sh
index 2e1a6f0c34..a99198b0f6 100755
--- a/t/t1518-repo-info.sh
+++ b/t/t1518-repo-info.sh
@@ -6,6 +6,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 
+DEFAULT_NUMBER_OF_FIELDS=1
+
 parse_json () {
 	tr '\n' ' ' | "$PERL_PATH" "$TEST_DIRECTORY/t0019/parse_json.perl"
 }
@@ -46,4 +48,22 @@ test_expect_success 'plaintext: returns empty output with allow-empty' '
 	test_line_count = 0 output
 '
 
+test_repo_info 'ref format files is retrieved correctly' \
+	'' \
+	'references.format' 'files'
+
+test_repo_info 'ref format reftable is retrieved correctly' \
+	'--ref-format=reftable' \
+	'references.format' 'reftable'
+
+test_expect_success 'plaintext: output all default fields' "
+	git repo-info --format=plaintext >actual &&
+	test_line_count = $DEFAULT_NUMBER_OF_FIELDS actual
+"
+
+test_expect_success 'json: output all default fields' "
+	git repo-info --format=json | parse_json | grep '.*\..*\..*' >actual &&
+	test_line_count = $DEFAULT_NUMBER_OF_FIELDS actual
+"
+
 test_done
-- 
2.39.5 (Apple Git-154)