cat-file: add %(objectmode) atom

Add a formatting atom, used with the --batch-check/--batch-command options, that prints the octal representation of the object mode if a given revision includes that information, e.g. one that follows the format <tree-ish>:<path>. If the mode information does not exist, an empty string is printed instead. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Victoria Dye <vdye@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Victoria Dye committed Jun 2, 2025 at 18:55 UTC aba14384354c6f722c127962bc756a542078f3e3
3 files changed +34 -18
Documentation/git-cat-file.adoc
+5
@@ -307,6 +307,11 @@ newline. The available atoms are:
307 `objecttype`::
308 The type of the object (the same as `cat-file -t` reports).
309
310 +`objectmode`::
311 + If the specified object has mode information (such as a tree or
312 + index entry), the mode expressed as an octal integer. Otherwise,
313 + empty string.
314 +
315 `objectsize`::
316 The size, in bytes, of the object (the same as `cat-file -s`
317 reports).
builtin/cat-file.c
+7 -2
@@ -275,6 +275,7 @@ struct expand_data {
275 struct object_id oid;
276 enum object_type type;
277 unsigned long size;
278 + unsigned short mode;
279 off_t disk_size;
280 const char *rest;
281 struct object_id delta_base_oid;
@@ -306,6 +307,7 @@ struct expand_data {
307 */
308 unsigned skip_object_info : 1;
309 };
310 +#define EXPAND_DATA_INIT { .mode = S_IFINVALID }
311
312 static int is_atom(const char *atom, const char *s, int slen)
313 {
@@ -345,6 +347,9 @@ static int expand_atom(struct strbuf *sb, const char *atom, int len,
347 else
348 strbuf_addstr(sb,
349 oid_to_hex(&data->delta_base_oid));
350 + } else if (is_atom("objectmode", atom, len)) {
351 + if (!data->mark_query && !(S_IFINVALID == data->mode))
352 + strbuf_addf(sb, "%06o", data->mode);
353 } else
354 return 0;
355 return 1;
@@ -613,6 +618,7 @@ static void batch_one_object(const char *obj_name,
618 goto out;
619 }
620
621 + data->mode = ctx.mode;
622 batch_object_write(obj_name, scratch, opt, data, NULL, 0);
623
624 out:
@@ -866,7 +872,7 @@ static int batch_objects(struct batch_options *opt)
872 {
873 struct strbuf input = STRBUF_INIT;
874 struct strbuf output = STRBUF_INIT;
869 - struct expand_data data;
875 + struct expand_data data = EXPAND_DATA_INIT;
876 int save_warning;
877 int retval = 0;
878
@@ -875,7 +881,6 @@ static int batch_objects(struct batch_options *opt)
881 * object_info to be handed to oid_object_info_extended for each
882 * object.
883 */
878 - memset(&data, 0, sizeof(data));
884 data.mark_query = 1;
885 expand_format(&output,
886 opt->format ? opt->format : DEFAULT_FORMAT,
t/t1006-cat-file.sh
+22 -16
@@ -114,10 +114,11 @@ strlen () {
114 run_tests () {
115 type=$1
116 object_name="$2"
117 - size=$3
118 - content=$4
119 - pretty_content=$5
120 - oid=${6:-"$object_name"}
117 + mode=$3
118 + size=$4
119 + content=$5
120 + pretty_content=$6
121 + oid=${7:-"$object_name"}
122
123 batch_output="$oid $type $size
124 $content"
@@ -209,6 +210,12 @@ $content"
210 test_cmp expect actual
211 '
212
213 + test_expect_success '--batch-check with %(objectmode)' '
214 + echo "$mode $oid" >expect &&
215 + echo $object_name | git cat-file --batch-check="%(objectmode) %(objectname)" >actual &&
216 + test_cmp expect actual
217 + '
218 +
219 test -z "$content" ||
220 test_expect_success "--batch without type ($type)" '
221 {
@@ -247,8 +254,7 @@ test_expect_success "setup" '
254
255 run_blob_tests () {
256 oid=$1
250 -
251 - run_tests 'blob' $oid $hello_size "$hello_content" "$hello_content"
257 + run_tests 'blob' $oid "" $hello_size "$hello_content" "$hello_content"
258
259 test_expect_success '--batch-command --buffer with flush for blob info' '
260 echo "$oid blob $hello_size" >expect &&
@@ -286,12 +292,12 @@ tree_compat_size=$((2 * $(test_oid --hash=compat rawsz) + 13 + 24))
292 tree_pretty_content="100644 blob $hello_oid hello${LF}100755 blob $hello_oid path with spaces${LF}"
293 tree_compat_pretty_content="100644 blob $hello_compat_oid hello${LF}100755 blob $hello_compat_oid path with spaces${LF}"
294
289 -run_tests 'tree' $tree_oid $tree_size "" "$tree_pretty_content"
290 -run_tests 'tree' $tree_compat_oid $tree_compat_size "" "$tree_compat_pretty_content"
291 -run_tests 'blob' "$tree_oid:hello" $hello_size "" "$hello_content" $hello_oid
292 -run_tests 'blob' "$tree_compat_oid:hello" $hello_size "" "$hello_content" $hello_compat_oid
293 -run_tests 'blob' "$tree_oid:path with spaces" $hello_size "" "$hello_content" $hello_oid
294 -run_tests 'blob' "$tree_compat_oid:path with spaces" $hello_size "" "$hello_content" $hello_compat_oid
295 +run_tests 'tree' $tree_oid "" $tree_size "" "$tree_pretty_content"
296 +run_tests 'tree' $tree_compat_oid "" $tree_compat_size "" "$tree_compat_pretty_content"
297 +run_tests 'blob' "$tree_oid:hello" "100644" $hello_size "" "$hello_content" $hello_oid
298 +run_tests 'blob' "$tree_compat_oid:hello" "100644" $hello_size "" "$hello_content" $hello_compat_oid
299 +run_tests 'blob' "$tree_oid:path with spaces" "100755" $hello_size "" "$hello_content" $hello_oid
300 +run_tests 'blob' "$tree_compat_oid:path with spaces" "100755" $hello_size "" "$hello_content" $hello_compat_oid
301
302 commit_message="Initial commit"
303 commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid)
@@ -310,8 +316,8 @@ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
316
317 $commit_message"
318
313 -run_tests 'commit' $commit_oid $commit_size "$commit_content" "$commit_content"
314 -run_tests 'commit' $commit_compat_oid $commit_compat_size "$commit_compat_content" "$commit_compat_content"
319 +run_tests 'commit' $commit_oid "" $commit_size "$commit_content" "$commit_content"
320 +run_tests 'commit' $commit_compat_oid "" $commit_compat_size "$commit_compat_content" "$commit_compat_content"
321
322 tag_header_without_oid="type blob
323 tag hellotag
@@ -334,8 +340,8 @@ tag_size=$(strlen "$tag_content")
340 tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
341 tag_compat_size=$(strlen "$tag_compat_content")
342
337 -run_tests 'tag' $tag_oid $tag_size "$tag_content" "$tag_content"
338 -run_tests 'tag' $tag_compat_oid $tag_compat_size "$tag_compat_content" "$tag_compat_content"
343 +run_tests 'tag' $tag_oid "" $tag_size "$tag_content" "$tag_content"
344 +run_tests 'tag' $tag_compat_oid "" $tag_compat_size "$tag_compat_content" "$tag_compat_content"
345
346 test_expect_success "Reach a blob from a tag pointing to it" '
347 echo_without_newline "$hello_content" >expect &&