{"thread":{"id":"63566","subject":"[PATCH 0/3] cat-file: add %(objectmode) and submodule message to batch commands","startedAt":"2025-06-02T18:55:58Z","lastAt":"2025-06-05T07:51:14Z","messageCount":10,"participants":["Victoria Dye via GitGitGadget","Junio C Hamano","Jeff King","Victoria Dye"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"519492","messageId":"pull.1929.git.1748890555.gitgitgadget@gmail.com","threadId":"63566","inReplyTo":null,"subject":"[PATCH 0/3] cat-file: add %(objectmode) and submodule message to batch commands","fromName":"Victoria Dye via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-06-02T18:55:52Z","receivedAt":"2025-06-02T18:55:58Z","isPatch":true,"sender":{"key":"vdye@github.com","avatar":"https://avatars.githubusercontent.com/u/3619353?v=4"},"body":"This series re-attempts the changes proposed last year [1] for extending the\ninformation about tree entries available from the 'cat-file' batch format\ncommands. It also (hopefully) addresses the initial round of feedback that\nseries received.\n\nThe first patch updates 't1006-cat-file.sh' to test non-OID object\nspecifications. In response to the feedback in [2], I added more careful\nquoting and a couple tests using paths with spaces. This change revealed a\n(likely known) limitation of the '%(rest)' atom when processing object names\nwith spaces. To make that limitation explicit, I marked the relevant test as\nexpected to fail.\n\nThe second patch adds \"mode\" support. This is essentially unchanged from its\ninitial submission, save for some conflict resolution in the test script.\n\nThe final patch takes a different approach to submodule resolution than the\ninitial submission; rather than treat the entry as a \"regular\" commit object\nwith empty content, we now print an error message similar to the \"missing\",\n\"ambiguous\", etc. cases, but with the tree entry's OID rather than the input\nobject name.\n\nAs for the motivation behind the change (re: [3]), the goal of this series\nis to be able to get more of the information available internally about an\nobject in 'cat-file --batch[*]' -- in the case of a tree entry, the main\nthings missing were the file mode and the presence (and OID) of submodule\npointers. As Junio mentioned in [4], using a single long-running process to\nresolve objects is far more performant than spawning multiple processes to\nresolve tree entries with something like 'ls-tree', especially when\nresolving entries across multiple trees or resolving a mix of tree entries\nand OIDs, refnames, etc. The object resolution logic in 'cat-file' meant\nthat the mode & submodule OID information were already (mostly) available,\nbut we didn't have a way to output it.\n\nThe intent of this series is to make the new format options/outputs to get\nthose fields as unobtrusive as possible, but I'm happy to do something more\nlike the previous series if that would be preferable.\n\n[1]\nhttps://lore.kernel.org/git/pull.1689.git.1710183362.gitgitgadget@gmail.com/\n[2] https://lore.kernel.org/git/xmqqle6oo2ns.fsf@gitster.g/ [3]\nhttps://lore.kernel.org/git/20240312221758.GA109417@coredump.intra.peff.net/\n[4] https://lore.kernel.org/git/xmqq1q8fl05r.fsf@gitster.g/\n\nVictoria Dye (3):\n  t1006: update 'run_tests' to test generic object specifiers\n  cat-file: add %(objectmode) atom\n  cat-file.c: add batch handling for submodules\n\n Documentation/git-cat-file.adoc |  13 ++++\n builtin/cat-file.c              |  14 +++-\n t/t1006-cat-file.sh             | 111 +++++++++++++++++++++++---------\n 3 files changed, 103 insertions(+), 35 deletions(-)\n\n\nbase-commit: 7014b55638da979331baf8dc31c4e1d697cf2d67\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1929%2Fvdye%2Fvdye%2Fcat-file-mode-submodule-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1929/vdye/vdye/cat-file-mode-submodule-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1929\n-- \ngitgitgadget\n"},{"id":"519493","messageId":"e05c5bea7143d54a5cdb2d9cbfe944409c891797.1748890555.git.gitgitgadget@gmail.com","threadId":"63566","inReplyTo":"pull.1929.git.1748890555.gitgitgadget@gmail.com","subject":"[PATCH 1/3] t1006: update 'run_tests' to test generic object specifiers","fromName":"Victoria Dye via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-06-02T18:55:53Z","receivedAt":"2025-06-02T18:55:59Z","isPatch":true,"sender":{"key":"vdye@github.com","avatar":"https://avatars.githubusercontent.com/u/3619353?v=4"},"body":"From: Victoria Dye <vdye@github.com>\n\nUpdate the 'run_tests' test wrapper so that the first argument may refer to\nany specifier that uniquely identifies an object (e.g. a ref name,\n'<OID>:<path>', '<OID>^{<type>}', etc.), rather than only a full object ID.\n\nAlso add tests that use non-OID identifiers, ensuring appropriate parsing in\n'cat-file'. The identifiers used in some of the added tests include a space,\nwhich is incompatible with the '%(rest)' atom. To accommodate that without\nremoving the test case, use 'test_expect_failure' when 'object_name'\nincludes a space.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Victoria Dye <vdye@github.com>\n---\n t/t1006-cat-file.sh | 56 +++++++++++++++++++++++++++++----------------\n 1 file changed, 36 insertions(+), 20 deletions(-)\n\ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 317da6869c88..7c9512a6b439 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -113,53 +113,54 @@ strlen () {\n \n run_tests () {\n     type=$1\n-    oid=$2\n+    object_name=\"$2\"\n     size=$3\n     content=$4\n     pretty_content=$5\n+    oid=${6:-\"$object_name\"}\n \n     batch_output=\"$oid $type $size\n $content\"\n \n     test_expect_success \"$type exists\" '\n-\tgit cat-file -e $oid\n+\tgit cat-file -e \"$object_name\"\n     '\n \n     test_expect_success \"Type of $type is correct\" '\n \techo $type >expect &&\n-\tgit cat-file -t $oid >actual &&\n+\tgit cat-file -t \"$object_name\" >actual &&\n \ttest_cmp expect actual\n     '\n \n     test_expect_success \"Size of $type is correct\" '\n \techo $size >expect &&\n-\tgit cat-file -s $oid >actual &&\n+\tgit cat-file -s \"$object_name\" >actual &&\n \ttest_cmp expect actual\n     '\n \n     test -z \"$content\" ||\n     test_expect_success \"Content of $type is correct\" '\n \techo_without_newline \"$content\" >expect &&\n-\tgit cat-file $type $oid >actual &&\n+\tgit cat-file $type \"$object_name\" >actual &&\n \ttest_cmp expect actual\n     '\n \n     test_expect_success \"Pretty content of $type is correct\" '\n \techo_without_newline \"$pretty_content\" >expect &&\n-\tgit cat-file -p $oid >actual &&\n+\tgit cat-file -p \"$object_name\" >actual &&\n \ttest_cmp expect actual\n     '\n \n     test -z \"$content\" ||\n     test_expect_success \"--batch output of $type is correct\" '\n \techo \"$batch_output\" >expect &&\n-\techo $oid | git cat-file --batch >actual &&\n+\techo \"$object_name\" | git cat-file --batch >actual &&\n \ttest_cmp expect actual\n     '\n \n     test_expect_success \"--batch-check output of $type is correct\" '\n \techo \"$oid $type $size\" >expect &&\n-\techo_without_newline $oid | git cat-file --batch-check >actual &&\n+\techo_without_newline \"$object_name\" | git cat-file --batch-check >actual &&\n \ttest_cmp expect actual\n     '\n \n@@ -168,13 +169,13 @@ $content\"\n \ttest -z \"$content\" ||\n \t\ttest_expect_success \"--batch-command $opt output of $type content is correct\" '\n \t\techo \"$batch_output\" >expect &&\n-\t\ttest_write_lines \"contents $oid\" | git cat-file --batch-command $opt >actual &&\n+\t\ttest_write_lines \"contents $object_name\" | git cat-file --batch-command $opt >actual &&\n \t\ttest_cmp expect actual\n \t'\n \n \ttest_expect_success \"--batch-command $opt output of $type info is correct\" '\n \t\techo \"$oid $type $size\" >expect &&\n-\t\ttest_write_lines \"info $oid\" |\n+\t\ttest_write_lines \"info $object_name\" |\n \t\tgit cat-file --batch-command $opt >actual &&\n \t\ttest_cmp expect actual\n \t'\n@@ -182,19 +183,28 @@ $content\"\n \n     test_expect_success \"custom --batch-check format\" '\n \techo \"$type $oid\" >expect &&\n-\techo $oid | git cat-file --batch-check=\"%(objecttype) %(objectname)\" >actual &&\n+\techo \"$object_name\" | git cat-file --batch-check=\"%(objecttype) %(objectname)\" >actual &&\n \ttest_cmp expect actual\n     '\n \n     test_expect_success \"custom --batch-command format\" '\n \techo \"$type $oid\" >expect &&\n-\techo \"info $oid\" | git cat-file --batch-command=\"%(objecttype) %(objectname)\" >actual &&\n+\techo \"info $object_name\" | git cat-file --batch-command=\"%(objecttype) %(objectname)\" >actual &&\n \ttest_cmp expect actual\n     '\n \n-    test_expect_success '--batch-check with %(rest)' '\n+    # FIXME: %(rest) is incompatible with object names that include whitespace,\n+    # e.g. HEAD:path/to/a/file with spaces. Use the resolved OID as input to\n+    # test this instead of the raw object name.\n+    if echo \"$object_name\" | grep \" \"; then\n+\ttest_rest=test_expect_failure\n+    else\n+\ttest_rest=test_expect_success\n+    fi\n+\n+    $test_rest '--batch-check with %(rest)' '\n \techo \"$type this is some extra content\" >expect &&\n-\techo \"$oid    this is some extra content\" |\n+\techo \"$object_name    this is some extra content\" |\n \t\tgit cat-file --batch-check=\"%(objecttype) %(rest)\" >actual &&\n \ttest_cmp expect actual\n     '\n@@ -205,7 +215,7 @@ $content\"\n \t\techo \"$size\" &&\n \t\techo \"$content\"\n \t} >expect &&\n-\techo $oid | git cat-file --batch=\"%(objectsize)\" >actual &&\n+\techo \"$object_name\" | git cat-file --batch=\"%(objectsize)\" >actual &&\n \ttest_cmp expect actual\n     '\n \n@@ -215,7 +225,7 @@ $content\"\n \t\techo \"$type\" &&\n \t\techo \"$content\"\n \t} >expect &&\n-\techo $oid | git cat-file --batch=\"%(objecttype)\" >actual &&\n+\techo \"$object_name\" | git cat-file --batch=\"%(objecttype)\" >actual &&\n \ttest_cmp expect actual\n     '\n }\n@@ -230,6 +240,8 @@ test_expect_success \"setup\" '\n \tgit config extensions.compatobjectformat $test_compat_hash_algo &&\n \techo_without_newline \"$hello_content\" > hello &&\n \tgit update-index --add hello &&\n+\techo_without_newline \"$hello_content\" > \"path with spaces\" &&\n+\tgit update-index --add --chmod=+x \"path with spaces\" &&\n \tgit commit -m \"add hello file\"\n '\n \n@@ -269,13 +281,17 @@ test_expect_success '--batch-check without %(rest) considers whole line' '\n \n tree_oid=$(git write-tree)\n tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid)\n-tree_size=$(($(test_oid rawsz) + 13))\n-tree_compat_size=$(($(test_oid --hash=compat rawsz) + 13))\n-tree_pretty_content=\"100644 blob $hello_oid\thello${LF}\"\n-tree_compat_pretty_content=\"100644 blob $hello_compat_oid\thello${LF}\"\n+tree_size=$((2 * $(test_oid rawsz) + 13 + 24))\n+tree_compat_size=$((2 * $(test_oid --hash=compat rawsz) + 13 + 24))\n+tree_pretty_content=\"100644 blob $hello_oid\thello${LF}100755 blob $hello_oid\tpath with spaces${LF}\"\n+tree_compat_pretty_content=\"100644 blob $hello_compat_oid\thello${LF}100755 blob $hello_compat_oid\tpath with spaces${LF}\"\n \n run_tests 'tree' $tree_oid $tree_size \"\" \"$tree_pretty_content\"\n run_tests 'tree' $tree_compat_oid $tree_compat_size \"\" \"$tree_compat_pretty_content\"\n+run_tests 'blob' \"$tree_oid:hello\" $hello_size \"\" \"$hello_content\" $hello_oid\n+run_tests 'blob' \"$tree_compat_oid:hello\" $hello_size \"\" \"$hello_content\" $hello_compat_oid\n+run_tests 'blob' \"$tree_oid:path with spaces\" $hello_size \"\" \"$hello_content\" $hello_oid\n+run_tests 'blob' \"$tree_compat_oid:path with spaces\" $hello_size \"\" \"$hello_content\" $hello_compat_oid\n \n commit_message=\"Initial commit\"\n commit_oid=$(echo_without_newline \"$commit_message\" | git commit-tree $tree_oid)\n-- \ngitgitgadget\n\n"},{"id":"519494","messageId":"1ff39aa3c6e613137edde9e05321a7df5c165e99.1748890555.git.gitgitgadget@gmail.com","threadId":"63566","inReplyTo":"pull.1929.git.1748890555.gitgitgadget@gmail.com","subject":"[PATCH 2/3] cat-file: add %(objectmode) atom","fromName":"Victoria Dye via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-06-02T18:55:54Z","receivedAt":"2025-06-02T18:56:00Z","isPatch":true,"sender":{"key":"vdye@github.com","avatar":"https://avatars.githubusercontent.com/u/3619353?v=4"},"body":"From: Victoria Dye <vdye@github.com>\n\nAdd a formatting atom, used with the --batch-check/--batch-command options,\nthat prints the octal representation of the object mode if a given revision\nincludes that information, e.g. one that follows the format\n<tree-ish>:<path>. If the mode information does not exist, an empty string\nis printed instead.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Victoria Dye <vdye@github.com>\n---\n Documentation/git-cat-file.adoc |  5 +++++\n builtin/cat-file.c              |  9 ++++++--\n t/t1006-cat-file.sh             | 38 +++++++++++++++++++--------------\n 3 files changed, 34 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-cat-file.adoc b/Documentation/git-cat-file.adoc\nindex cde79ad242bb..5c002c0499e4 100644\n--- a/Documentation/git-cat-file.adoc\n+++ b/Documentation/git-cat-file.adoc\n@@ -307,6 +307,11 @@ newline. The available atoms are:\n `objecttype`::\n \tThe type of the object (the same as `cat-file -t` reports).\n \n+`objectmode`::\n+\tIf the specified object has mode information (such as a tree or\n+\tindex entry), the mode expressed as an octal integer. Otherwise,\n+\tempty string.\n+\n `objectsize`::\n \tThe size, in bytes, of the object (the same as `cat-file -s`\n \treports).\ndiff --git a/builtin/cat-file.c b/builtin/cat-file.c\nindex 67a5ff2b9ebd..b11576756bcc 100644\n--- a/builtin/cat-file.c\n+++ b/builtin/cat-file.c\n@@ -275,6 +275,7 @@ struct expand_data {\n \tstruct object_id oid;\n \tenum object_type type;\n \tunsigned long size;\n+\tunsigned short mode;\n \toff_t disk_size;\n \tconst char *rest;\n \tstruct object_id delta_base_oid;\n@@ -306,6 +307,7 @@ struct expand_data {\n \t */\n \tunsigned skip_object_info : 1;\n };\n+#define EXPAND_DATA_INIT  { .mode = S_IFINVALID }\n \n static int is_atom(const char *atom, const char *s, int slen)\n {\n@@ -345,6 +347,9 @@ static int expand_atom(struct strbuf *sb, const char *atom, int len,\n \t\telse\n \t\t\tstrbuf_addstr(sb,\n \t\t\t\t      oid_to_hex(&data->delta_base_oid));\n+\t} else if (is_atom(\"objectmode\", atom, len)) {\n+\t\tif (!data->mark_query && !(S_IFINVALID == data->mode))\n+\t\t\tstrbuf_addf(sb, \"%06o\", data->mode);\n \t} else\n \t\treturn 0;\n \treturn 1;\n@@ -613,6 +618,7 @@ static void batch_one_object(const char *obj_name,\n \t\tgoto out;\n \t}\n \n+\tdata->mode = ctx.mode;\n \tbatch_object_write(obj_name, scratch, opt, data, NULL, 0);\n \n out:\n@@ -866,7 +872,7 @@ static int batch_objects(struct batch_options *opt)\n {\n \tstruct strbuf input = STRBUF_INIT;\n \tstruct strbuf output = STRBUF_INIT;\n-\tstruct expand_data data;\n+\tstruct expand_data data = EXPAND_DATA_INIT;\n \tint save_warning;\n \tint retval = 0;\n \n@@ -875,7 +881,6 @@ static int batch_objects(struct batch_options *opt)\n \t * object_info to be handed to oid_object_info_extended for each\n \t * object.\n \t */\n-\tmemset(&data, 0, sizeof(data));\n \tdata.mark_query = 1;\n \texpand_format(&output,\n \t\t      opt->format ? opt->format : DEFAULT_FORMAT,\ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 7c9512a6b439..97052b3f31f1 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -114,10 +114,11 @@ strlen () {\n run_tests () {\n     type=$1\n     object_name=\"$2\"\n-    size=$3\n-    content=$4\n-    pretty_content=$5\n-    oid=${6:-\"$object_name\"}\n+    mode=$3\n+    size=$4\n+    content=$5\n+    pretty_content=$6\n+    oid=${7:-\"$object_name\"}\n \n     batch_output=\"$oid $type $size\n $content\"\n@@ -209,6 +210,12 @@ $content\"\n \ttest_cmp expect actual\n     '\n \n+    test_expect_success '--batch-check with %(objectmode)' '\n+\techo \"$mode $oid\" >expect &&\n+\techo $object_name | git cat-file --batch-check=\"%(objectmode) %(objectname)\" >actual &&\n+\ttest_cmp expect actual\n+    '\n+\n     test -z \"$content\" ||\n     test_expect_success \"--batch without type ($type)\" '\n \t{\n@@ -247,8 +254,7 @@ test_expect_success \"setup\" '\n \n run_blob_tests () {\n     oid=$1\n-\n-    run_tests 'blob' $oid $hello_size \"$hello_content\" \"$hello_content\"\n+    run_tests 'blob' $oid \"\" $hello_size \"$hello_content\" \"$hello_content\"\n \n     test_expect_success '--batch-command --buffer with flush for blob info' '\n \techo \"$oid blob $hello_size\" >expect &&\n@@ -286,12 +292,12 @@ tree_compat_size=$((2 * $(test_oid --hash=compat rawsz) + 13 + 24))\n tree_pretty_content=\"100644 blob $hello_oid\thello${LF}100755 blob $hello_oid\tpath with spaces${LF}\"\n tree_compat_pretty_content=\"100644 blob $hello_compat_oid\thello${LF}100755 blob $hello_compat_oid\tpath with spaces${LF}\"\n \n-run_tests 'tree' $tree_oid $tree_size \"\" \"$tree_pretty_content\"\n-run_tests 'tree' $tree_compat_oid $tree_compat_size \"\" \"$tree_compat_pretty_content\"\n-run_tests 'blob' \"$tree_oid:hello\" $hello_size \"\" \"$hello_content\" $hello_oid\n-run_tests 'blob' \"$tree_compat_oid:hello\" $hello_size \"\" \"$hello_content\" $hello_compat_oid\n-run_tests 'blob' \"$tree_oid:path with spaces\" $hello_size \"\" \"$hello_content\" $hello_oid\n-run_tests 'blob' \"$tree_compat_oid:path with spaces\" $hello_size \"\" \"$hello_content\" $hello_compat_oid\n+run_tests 'tree' $tree_oid \"\" $tree_size \"\" \"$tree_pretty_content\"\n+run_tests 'tree' $tree_compat_oid \"\" $tree_compat_size \"\" \"$tree_compat_pretty_content\"\n+run_tests 'blob' \"$tree_oid:hello\" \"100644\" $hello_size \"\" \"$hello_content\" $hello_oid\n+run_tests 'blob' \"$tree_compat_oid:hello\" \"100644\" $hello_size \"\" \"$hello_content\" $hello_compat_oid\n+run_tests 'blob' \"$tree_oid:path with spaces\" \"100755\" $hello_size \"\" \"$hello_content\" $hello_oid\n+run_tests 'blob' \"$tree_compat_oid:path with spaces\" \"100755\" $hello_size \"\" \"$hello_content\" $hello_compat_oid\n \n commit_message=\"Initial commit\"\n commit_oid=$(echo_without_newline \"$commit_message\" | git commit-tree $tree_oid)\n@@ -310,8 +316,8 @@ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n \n $commit_message\"\n \n-run_tests 'commit' $commit_oid $commit_size \"$commit_content\" \"$commit_content\"\n-run_tests 'commit' $commit_compat_oid $commit_compat_size \"$commit_compat_content\" \"$commit_compat_content\"\n+run_tests 'commit' $commit_oid \"\" $commit_size \"$commit_content\" \"$commit_content\"\n+run_tests 'commit' $commit_compat_oid \"\" $commit_compat_size \"$commit_compat_content\" \"$commit_compat_content\"\n \n tag_header_without_oid=\"type blob\n tag hellotag\n@@ -334,8 +340,8 @@ tag_size=$(strlen \"$tag_content\")\n tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)\n tag_compat_size=$(strlen \"$tag_compat_content\")\n \n-run_tests 'tag' $tag_oid $tag_size \"$tag_content\" \"$tag_content\"\n-run_tests 'tag' $tag_compat_oid $tag_compat_size \"$tag_compat_content\" \"$tag_compat_content\"\n+run_tests 'tag' $tag_oid \"\" $tag_size \"$tag_content\" \"$tag_content\"\n+run_tests 'tag' $tag_compat_oid \"\" $tag_compat_size \"$tag_compat_content\" \"$tag_compat_content\"\n \n test_expect_success \"Reach a blob from a tag pointing to it\" '\n \techo_without_newline \"$hello_content\" >expect &&\n-- \ngitgitgadget\n\n"},{"id":"519495","messageId":"980ab7f7ef56944df78530dcc9c79b54d1450806.1748890555.git.gitgitgadget@gmail.com","threadId":"63566","inReplyTo":"pull.1929.git.1748890555.gitgitgadget@gmail.com","subject":"[PATCH 3/3] cat-file.c: add batch handling for submodules","fromName":"Victoria Dye via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-06-02T18:55:55Z","receivedAt":"2025-06-02T18:56:01Z","isPatch":true,"sender":{"key":"vdye@github.com","avatar":"https://avatars.githubusercontent.com/u/3619353?v=4"},"body":"From: Victoria Dye <vdye@github.com>\n\nWhen an object specification is passed to 'cat-file --batch[-check]'\nreferring to a submodule (e.g. 'HEAD:path/to/my/submodule'), the current\nbehavior of the command is to print the \"missing\" error message. However, it\nis often valuable for callers to distinguish between paths that are actually\nmissing and \"the submodule tree entry exists, but the object does not exist\nin the repository\".\n\nTo disambiguate without needing to invoke a separate Git process (e.g.\n'ls-tree'), print the message \"<oid> submodule\" for such objects instead of\n\"<object> missing\". In addition to the change from \"missing\" to \"submodule\",\nthe new message differs from the old in that it always prints the resolved\ntree entry's OID, rather than the input object specification.\n\nNote that this implementation maintains a distinction between submodules\nwhere the commit OID is not present in the repo, and submodules where the\ncommit OID *is* present; the former will now print \"<object> submodule\", but\nthe latter will still print the full object content.\n\nSigned-off-by: Victoria Dye <vdye@github.com>\n---\n Documentation/git-cat-file.adoc |  8 ++++++++\n builtin/cat-file.c              |  5 ++++-\n t/t1006-cat-file.sh             | 25 +++++++++++++++++++++++++\n 3 files changed, 37 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-cat-file.adoc b/Documentation/git-cat-file.adoc\nindex 5c002c0499e4..180d1ad363fd 100644\n--- a/Documentation/git-cat-file.adoc\n+++ b/Documentation/git-cat-file.adoc\n@@ -373,6 +373,14 @@ If a name is specified that might refer to more than one object (an ambiguous sh\n <object> SP ambiguous LF\n ------------\n \n+If a name is specified that refers to a submodule entry in a tree and the\n+target object does not exist in the repository, then `cat-file` will ignore\n+any custom format and print (with the object ID of the submodule):\n+\n+------------\n+<oid> SP submodule LF\n+------------\n+\n If `--follow-symlinks` is used, and a symlink in the repository points\n outside the repository, then `cat-file` will ignore any custom format\n and print:\ndiff --git a/builtin/cat-file.c b/builtin/cat-file.c\nindex b11576756bcc..4b23fcecbd8e 100644\n--- a/builtin/cat-file.c\n+++ b/builtin/cat-file.c\n@@ -496,7 +496,10 @@ static void batch_object_write(const char *obj_name,\n \t\t\t\t\t\t       &data->oid, &data->info,\n \t\t\t\t\t\t       OBJECT_INFO_LOOKUP_REPLACE);\n \t\tif (ret < 0) {\n-\t\t\treport_object_status(opt, obj_name, &data->oid, \"missing\");\n+\t\t\tif (data->mode == S_IFGITLINK)\n+\t\t\t\treport_object_status(opt, oid_to_hex(&data->oid), &data->oid, \"submodule\");\n+\t\t\telse\n+\t\t\t\treport_object_status(opt, obj_name, &data->oid, \"missing\");\n \t\t\treturn;\n \t\t}\n \ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 97052b3f31f1..f123ef1e360a 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -1220,6 +1220,31 @@ test_expect_success 'cat-file --batch-check respects replace objects' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'batch-check with a submodule' '\n+\t# FIXME: this call to mktree is incompatible with compatObjectFormat\n+\t# because the submodule OID cannot be mapped to the compat hash algo.\n+\ttest_unconfig extensions.compatobjectformat &&\n+\tprintf \"160000 commit $(test_oid deadbeef)\\tsub\\n\" >tree-with-sub &&\n+\ttree=$(git mktree <tree-with-sub) &&\n+\ttest_config extensions.compatobjectformat $test_compat_hash_algo &&\n+\n+\tgit cat-file --batch-check >actual <<-EOF &&\n+\t$tree:sub\n+\tEOF\n+\tprintf \"$(test_oid deadbeef) submodule\\n\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'batch-check with a submodule, object exists' '\n+\tprintf \"160000 commit $commit_oid\\tsub\\n\" >tree-with-sub &&\n+\ttree=$(git mktree <tree-with-sub) &&\n+\tgit cat-file --batch-check >actual <<-EOF &&\n+\t$tree:sub\n+\tEOF\n+\tprintf \"$commit_oid commit $commit_size\\n\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n # Pull the entry for object with oid \"$1\" out of the output of\n # \"cat-file --batch\", including its object content (which requires\n # parsing and reading a set amount of bytes, hence perl).\n-- \ngitgitgadget\n"},{"id":"519672","messageId":"xmqqh60vefee.fsf@gitster.g","threadId":"63566","inReplyTo":"pull.1929.git.1748890555.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/3] cat-file: add %(objectmode) and submodule message to batch commands","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-04T14:43:21Z","receivedAt":"2025-06-04T14:43:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Victoria Dye via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> This series re-attempts the changes proposed last year [1] for extending the\n> information about tree entries available from the 'cat-file' batch format\n> commands. It also (hopefully) addresses the initial round of feedback that\n> series received.\n>\n> The first patch updates 't1006-cat-file.sh' to test non-OID object\n> specifications. In response to the feedback in [2], I added more careful\n> quoting and a couple tests using paths with spaces. This change revealed a\n> (likely known) limitation of the '%(rest)' atom when processing object names\n> with spaces. To make that limitation explicit, I marked the relevant test as\n> expected to fail.\n>\n> The second patch adds \"mode\" support. This is essentially unchanged from its\n> initial submission, save for some conflict resolution in the test script.\n>\n> The final patch takes a different approach to submodule resolution than the\n> initial submission; rather than treat the entry as a \"regular\" commit object\n> with empty content, we now print an error message similar to the \"missing\",\n> \"ambiguous\", etc. cases, but with the tree entry's OID rather than the input\n> object name.\n\nI did not send any line-by-line reviews, but after reading these\npatches I didn't see anything questionable.  Unless we see others\ncomments that need to be addressed, let's merge it to 'next' in\npreparation for the next cycle.\n\nThanks.\n"},{"id":"519688","messageId":"20250604193631.GA1500045@coredump.intra.peff.net","threadId":"63566","inReplyTo":"1ff39aa3c6e613137edde9e05321a7df5c165e99.1748890555.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/3] cat-file: add %(objectmode) atom","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-06-04T19:36:31Z","receivedAt":"2025-06-04T19:36:39Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jun 02, 2025 at 06:55:54PM +0000, Victoria Dye via GitGitGadget wrote:\n\n> Add a formatting atom, used with the --batch-check/--batch-command options,\n> that prints the octal representation of the object mode if a given revision\n> includes that information, e.g. one that follows the format\n> <tree-ish>:<path>. If the mode information does not exist, an empty string\n> is printed instead.\n\nOverall, this looks good to me. I have a few small comments below,\nthough I'm not sure if they merit a re-roll or not.\n\n> @@ -345,6 +347,9 @@ static int expand_atom(struct strbuf *sb, const char *atom, int len,\n>  \t\telse\n>  \t\t\tstrbuf_addstr(sb,\n>  \t\t\t\t      oid_to_hex(&data->delta_base_oid));\n> +\t} else if (is_atom(\"objectmode\", atom, len)) {\n> +\t\tif (!data->mark_query && !(S_IFINVALID == data->mode))\n> +\t\t\tstrbuf_addf(sb, \"%06o\", data->mode);\n>  \t} else\n>  \t\treturn 0;\n>  \treturn 1;\n\nLooking at this hunk raised a few questions. Fortunately with answers. ;)\n\nFirst, in other parts of this if/else chain, when mark_query is set we\nneed to perform some action (usually setting up the object_info\npointers). But we _don't_ need to do that here, since we get the mode\ninfo \"for free\" from get_oid_with_context(). Good.\n\nSecond, how do we reliably get S_IFINVALID? We can see that the\nexpand_data struct is now initialized with it:\n\n> +#define EXPAND_DATA_INIT  { .mode = S_IFINVALID }\n\nBut that seems like it would be a bug, since we only initialize it once,\nin batch_objects():\n\n> @@ -866,7 +872,7 @@ static int batch_objects(struct batch_options *opt)\n>  {\n>  \tstruct strbuf input = STRBUF_INIT;\n>  \tstruct strbuf output = STRBUF_INIT;\n> -\tstruct expand_data data;\n> +\tstruct expand_data data = EXPAND_DATA_INIT;\n>  \tint save_warning;\n>  \tint retval = 0;\n>  \n> @@ -875,7 +881,6 @@ static int batch_objects(struct batch_options *opt)\n>  \t * object_info to be handed to oid_object_info_extended for each\n>  \t * object.\n>  \t */\n> -\tmemset(&data, 0, sizeof(data));\n>  \tdata.mark_query = 1;\n>  \texpand_format(&output,\n>  \t\t      opt->format ? opt->format : DEFAULT_FORMAT,\n>  \n>  static int is_atom(const char *atom, const char *s, int slen)\n>  {\n\n...and then call batch_one_object() over and over. So at first glance,\ndoing this:\n\n  (echo HEAD:Makefile; echo HEAD) |\n  git cat-file --batch-check='%(objectmode)'\n\nwould let the mode from the first object bleed over into the second. But\nthat doesn't happen, because we overwrite expand_data.mode for each\nobject unconditionally, here:\n\n> @@ -613,6 +618,7 @@ static void batch_one_object(const char *obj_name,\n>  \t\tgoto out;\n>  \t}\n>  \n> +\tdata->mode = ctx.mode;\n>  \tbatch_object_write(obj_name, scratch, opt, data, NULL, 0);\n>  \n>  out:\n\nAnd there we are relying on ctx.mode, which we get from\nget_oid_with_context(), which always falls back to S_IFINVALID if no\nmode is available. Good.\n\nBut I think that means that the value set in EXPAND_DATA_INIT is never\nused, and we could continue to zero-initialize the struct with memset?\n\nThat said, it's probably OK to err on the side of over-initializing. The\nworst case is probably somebody later reading the code being confused\nabout the importance of the line. And at best it may prevent a future\ncode path from unexpectedly reading a funny value.\n\n\nAnd on to the third question. In the non-batch code path of\ncat_one_file(), we do:\n\n          if (obj_context.mode == S_IFINVALID)\n                  obj_context.mode = 0100644;\n\nwhich made me wonder if we should be harmonizing our behavior. But that\nmode is used only for passing to filter_object() and textconv_object().\nNeither of which really care about the mode, and this is mostly just\nsaying \"eh, do your regular thing as if it were a blob we found at\n--path\". I suspect we could get the same effect by just passing a\nhard-coded 100644 to those functions, but probably not worth changing\nnow (and certainly very orthogonal to your patch). But the important\nthing is we do not really need to worry about being consistent with this\nline. Good.\n\n-Peff\n"},{"id":"519690","messageId":"20250604195455.GB1500045@coredump.intra.peff.net","threadId":"63566","inReplyTo":"980ab7f7ef56944df78530dcc9c79b54d1450806.1748890555.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 3/3] cat-file.c: add batch handling for submodules","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-06-04T19:54:55Z","receivedAt":"2025-06-04T19:54:56Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jun 02, 2025 at 06:55:55PM +0000, Victoria Dye via GitGitGadget wrote:\n\n> To disambiguate without needing to invoke a separate Git process (e.g.\n> 'ls-tree'), print the message \"<oid> submodule\" for such objects instead of\n> \"<object> missing\". In addition to the change from \"missing\" to \"submodule\",\n> the new message differs from the old in that it always prints the resolved\n> tree entry's OID, rather than the input object specification.\n\nOK. I read over the discussion from last year, which I think mostly\ncentered around this patch. I do still think in the long run it would be\nnice for cat-file to produce what output it _can_ for a missing object\n(e.g., the oid and mode).\n\nBut I think it is OK to punt on that for now. Because \"<oid> missing\"\nlines already exist, we'd probably need to put such behavior behind a\nnew command-line option. So while \"<oid> submodule\" lines would be\nunnecessary in that hypothetical future world, we are not digging the\nhole any deeper, from a backwards-compatibility standpoint.\n\nAlthough speaking of backwards compatibility, I guess older readers may\nbe surprised that the old \"missing\" message becomes a \"submodule\" one.\nThey may need to be updated if they were written carefully to bail on\nunknown input (and were happy seeing \"missing\" messages for submodules).\nSo there may be some fallout, but it's not like the existing messages\nwere particularly useful in the first place.\n\n> Note that this implementation maintains a distinction between submodules\n> where the commit OID is not present in the repo, and submodules where the\n> commit OID *is* present; the former will now print \"<object> submodule\", but\n> the latter will still print the full object content.\n\nHmm, that is an interesting point. It feels kind of arbitrary, but I'm\nhaving trouble making a strong argument for one direction or the other.\nThe way you've written it means that readers need to be prepared to\nparse _both_ the mode and \"<oid> submodule\" lines to find submodules.\nBut maybe there's some value in finding out more information about\nsubmodule commits you do have in-repo.\n\nThe implementations are similar. Replacing this hunk:\n\n> diff --git a/builtin/cat-file.c b/builtin/cat-file.c\n> index b11576756bcc..4b23fcecbd8e 100644\n> --- a/builtin/cat-file.c\n> +++ b/builtin/cat-file.c\n> @@ -496,7 +496,10 @@ static void batch_object_write(const char *obj_name,\n>  \t\t\t\t\t\t       &data->oid, &data->info,\n>  \t\t\t\t\t\t       OBJECT_INFO_LOOKUP_REPLACE);\n>  \t\tif (ret < 0) {\n> -\t\t\treport_object_status(opt, obj_name, &data->oid, \"missing\");\n> +\t\t\tif (data->mode == S_IFGITLINK)\n> +\t\t\t\treport_object_status(opt, oid_to_hex(&data->oid), &data->oid, \"submodule\");\n> +\t\t\telse\n> +\t\t\t\treport_object_status(opt, obj_name, &data->oid, \"missing\");\n>  \t\t\treturn;\n>  \t\t}\n>  \n\nwith:\n\ndiff --git a/builtin/cat-file.c b/builtin/cat-file.c\nindex 4b23fcecbd..1b200e1607 100644\n--- a/builtin/cat-file.c\n+++ b/builtin/cat-file.c\n@@ -488,6 +488,11 @@ static void batch_object_write(const char *obj_name,\n \t\tif (opt->objects_filter.choice == LOFC_BLOB_LIMIT)\n \t\t\tdata->info.sizep = &data->size;\n \n+\t\tif (data->mode == S_IFGITLINK) {\n+\t\t\treport_object_status(opt, oid_to_hex(&data->oid), &data->oid, \"submodule\");\n+\t\t\treturn;\n+\t\t}\n+\n \t\tif (pack)\n \t\t\tret = packed_object_info(the_repository, pack, offset,\n \t\t\t\t\t\t &data->info);\n\nso I think the decision is really about what people will find most\nuseful. So I dunno. It is mostly a coin-flip, leading me to say that\nwhat you picked just came up \"heads\" and is good enough. ;)\n\n-Peff\n"},{"id":"519691","messageId":"20250604195701.GC1500045@coredump.intra.peff.net","threadId":"63566","inReplyTo":"xmqqh60vefee.fsf@gitster.g","subject":"Re: [PATCH 0/3] cat-file: add %(objectmode) and submodule message to batch commands","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-06-04T19:57:01Z","receivedAt":"2025-06-04T19:57:02Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 04, 2025 at 07:43:21AM -0700, Junio C Hamano wrote:\n\n> > The final patch takes a different approach to submodule resolution than the\n> > initial submission; rather than treat the entry as a \"regular\" commit object\n> > with empty content, we now print an error message similar to the \"missing\",\n> > \"ambiguous\", etc. cases, but with the tree entry's OID rather than the input\n> > object name.\n> \n> I did not send any line-by-line reviews, but after reading these\n> patches I didn't see anything questionable.  Unless we see others\n> comments that need to be addressed, let's merge it to 'next' in\n> preparation for the next cycle.\n\nI left a more detailed review. But it's mostly musing and self-answering\nquestions. I'd be OK to see this progress as-is.\n\n-Peff\n"},{"id":"519705","messageId":"2eb54073-20b3-465a-ad11-a2f22eb55930@github.com","threadId":"63566","inReplyTo":"20250604195455.GB1500045@coredump.intra.peff.net","subject":"Re: [PATCH 3/3] cat-file.c: add batch handling for submodules","fromName":"Victoria Dye","fromEmail":"vdye@github.com","sentAt":"2025-06-05T00:12:54Z","receivedAt":"2025-06-05T00:12:56Z","isPatch":true,"sender":{"key":"vdye@github.com","avatar":"https://avatars.githubusercontent.com/u/3619353?v=4"},"body":"Jeff King wrote:\n> On Mon, Jun 02, 2025 at 06:55:55PM +0000, Victoria Dye via GitGitGadget wrote:\n> \n>> To disambiguate without needing to invoke a separate Git process (e.g.\n>> 'ls-tree'), print the message \"<oid> submodule\" for such objects instead of\n>> \"<object> missing\". In addition to the change from \"missing\" to \"submodule\",\n>> the new message differs from the old in that it always prints the resolved\n>> tree entry's OID, rather than the input object specification.\n> \n> OK. I read over the discussion from last year, which I think mostly\n> centered around this patch. I do still think in the long run it would be\n> nice for cat-file to produce what output it _can_ for a missing object\n> (e.g., the oid and mode).\n\nOne way to handle that could be changing the message to something like:\n\nsubmodule SP <mode> SP <oid>\n\nbut...\n\n> \n> But I think it is OK to punt on that for now. Because \"<oid> missing\"\n> lines already exist, we'd probably need to put such behavior behind a\n> new command-line option. So while \"<oid> submodule\" lines would be\n> unnecessary in that hypothetical future world, we are not digging the\n> hole any deeper, from a backwards-compatibility standpoint.\n> \n> Although speaking of backwards compatibility, I guess older readers may\n> be surprised that the old \"missing\" message becomes a \"submodule\" one.\n> They may need to be updated if they were written carefully to bail on\n> unknown input (and were happy seeing \"missing\" messages for submodules).\n> So there may be some fallout, but it's not like the existing messages\n> were particularly useful in the first place.\n\n...I suspect that'd be even less compatible with existing automation around\n'cat-file' than just swapping out \"submodule\" for \"missing\", and users can\ntheoretically infer that the mode is 160000 (S_IFGITLINK). That said, if at\nsome point in the future we support submodules with a different mode, then\nan explicit value would be fairly useful.\n\nHappy to change it or keep it the same, I have no strong preference either\nway.\n\n> \n>> Note that this implementation maintains a distinction between submodules\n>> where the commit OID is not present in the repo, and submodules where the\n>> commit OID *is* present; the former will now print \"<object> submodule\", but\n>> the latter will still print the full object content.\n> \n> Hmm, that is an interesting point. It feels kind of arbitrary, but I'm\n> having trouble making a strong argument for one direction or the other.\n> The way you've written it means that readers need to be prepared to\n> parse _both_ the mode and \"<oid> submodule\" lines to find submodules.\n> But maybe there's some value in finding out more information about\n> submodule commits you do have in-repo.\n\nThis was pretty much my thought process on it. It was a somewhat arbitrary\nchoice, but what tipped me towards distinguishing the cases is that I'd\nrather have information like size, content, etc. about a commit and not need\nto use it, than need it but not have it available. That, and it does\nmaintain the existing treatment of self-referential submodules.\n"},{"id":"519732","messageId":"20250605075112.GC2066712@coredump.intra.peff.net","threadId":"63566","inReplyTo":"2eb54073-20b3-465a-ad11-a2f22eb55930@github.com","subject":"Re: [PATCH 3/3] cat-file.c: add batch handling for submodules","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-06-05T07:51:12Z","receivedAt":"2025-06-05T07:51:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 04, 2025 at 05:12:54PM -0700, Victoria Dye wrote:\n\n> > OK. I read over the discussion from last year, which I think mostly\n> > centered around this patch. I do still think in the long run it would be\n> > nice for cat-file to produce what output it _can_ for a missing object\n> > (e.g., the oid and mode).\n> \n> One way to handle that could be changing the message to something like:\n> \n> submodule SP <mode> SP <oid>\n\nHmm, yeah. That seemed weird to me at first because it doesn't\nnecessarily match what the caller asked for via batch-check. But really,\nmode and oid are the only reasonable things we could report anyway[1].\nAnd the mode is implicit in the word \"submodule\", so really there is\nonly the oid to report.\n\n[1] For now, at least. If we ever finally unify all of the various\n    formatting code, then one might in theory be able to feed a refname\n    to cat-file and print information about the ref, or perhaps other\n    meta-information. But let's not worry about that hypothetical for\n    now.\n\n> ...I suspect that'd be even less compatible with existing automation around\n> 'cat-file' than just swapping out \"submodule\" for \"missing\", and users can\n> theoretically infer that the mode is 160000 (S_IFGITLINK). That said, if at\n> some point in the future we support submodules with a different mode, then\n> an explicit value would be fairly useful.\n> \n> Happy to change it or keep it the same, I have no strong preference either\n> way.\n\nRight, that makes sense. I do wonder if:\n\n  <oid> missing submodule\n\nmight be friendlier to readers who are matching on /^[0-9a-f]+ missing/,\nbut now I am just guessing at a hypothetical program. So it may not be\nworth going down that rabbit hole, and we can just go with what you\nposted.\n\nWe can always worry about extending it later with an option to say \"turn\nplaceholders for missing objects into empty strings\" or similar.\n\nI did come across one other interesting case while thinking about this,\nthough. When running:\n\n  git cat-file --batch-check='%(objectname) %(objectmode)'\n\nwe do not need to access the object at all! So why does a submodule\nentry cause us to complain? The answer is that cat-file will (mostly for\nhistorical reasons) confirm the existence of the object name that is fed\nto it by calling oid_object_info(). The only exception is when we are\ndoing --batch-all-objects, since there we know we have the object,\nbecause we found it by iterating the odb. And we optimize out the extra\ncall for that case (which makes a big difference if you're just printing\nthe object names).\n\nBut since we don't expect submodule entries to exist in the first place,\nit might be reasonable to loosen that check. Something like this, though\nI think it could benefit from some refactoring:\n\ndiff --git a/builtin/cat-file.c b/builtin/cat-file.c\nindex 4b23fcecbd..bb52d9b673 100644\n--- a/builtin/cat-file.c\n+++ b/builtin/cat-file.c\n@@ -304,8 +304,20 @@ struct expand_data {\n \t * This flag will be true if the requested batch format and options\n \t * don't require us to call oid_object_info, which can then be\n \t * optimized out.\n+\t *\n+\t * The \"submodule\" variant is true if the format doesn't require it,\n+\t * but other options mean we'd usually continue to do so to check\n+\t * object existence. We can still omit the call for submodules in that\n+\t * case.\n+\t *\n+\t * This might be less confusing if we break skip_object_info down into\n+\t * two parts:\n+\t *   - does the format require oid_object_info?\n+\t *   - do the other options require checking existence?\n \t */\n \tunsigned skip_object_info : 1;\n+\tunsigned skip_submodule_info : 1;\n+\n };\n #define EXPAND_DATA_INIT  { .mode = S_IFINVALID }\n \n@@ -477,7 +489,8 @@ static void batch_object_write(const char *obj_name,\n \t\t\t       struct packed_git *pack,\n \t\t\t       off_t offset)\n {\n-\tif (!data->skip_object_info) {\n+\tif (!(data->skip_object_info ||\n+\t      (data->skip_submodule_info && data->mode == S_IFGITLINK))) {\n \t\tint ret;\n \n \t\tif (use_mailmap ||\n@@ -939,6 +952,12 @@ static int batch_objects(struct batch_options *opt)\n \n \t\tstrbuf_release(&output);\n \t\treturn 0;\n+\t} else {\n+\t\tstruct object_info empty = OBJECT_INFO_INIT;\n+\n+\t\tif (!memcmp(&data.info, &empty, sizeof(empty)) &&\n+\t\t    opt->objects_filter.choice == LOFC_DISABLED)\n+\t\t\tdata.skip_submodule_info = 1;\n \t}\n \n \t/*\n\nI don't think that needs to be part of your series, though. We'd still\npotentially need to handle the missing-submodule case for format\nrequests that actually look at the object, which would hit the \"<oid>\nsubmodule\" case you're adding. So it could come later (or not at all),\nand it's probably only worth pursuing if it would make life easier for\nyour intended caller.\n\n-Peff\n"}]}