git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v4 12/14] [GSOC] cat-file: reuse ref-filter logic

From
ZheNing Hu via GitGitGadget <gitgitgadget@gmail.com>
Date
Jun 22, 2021, 03:20 UTC
Message-ID
<258ec0a46c56c7e66710a2913efc4d4cbb1d5944.1624332055.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.980.v4.git.1624332054.gitgitgadget@gmail.com>
From: ZheNing Hu <adlternative@gmail.com>

In order to let cat-file use ref-filter logic, let's do the following:

1. Change the type of member `format` in struct `batch_options`
to `ref_format`, we will pass it to ref-filter later.
2. Let `batch_objects()` add atoms to format, and use
`verify_ref_format()` to check atoms.
3. Use `format_ref_array_item()` in `batch_object_write()` to
get the formatted data corresponding to the object. If the
return value of `format_ref_array_item()` is equals to zero,
use `batch_write()` to print object data; else if the return
value is less than zero, use `die()` to print the error message
and exit; else if return value is greater than zero, only print
the error message, but don't exit.
4. Use free_ref_array_item_value() to free ref_array_item's
value.

Most of the atoms in `for-each-ref --format` are now supported, such as `%(tree)`, `%(parent)`, `%(author)`, `%(tagger)`, `%(if)`, `%(then)`, `%(else)`, `%(end)`. But these atoms will be rejected: `%(refname)`, `%(symref)`, `%(upstream)`, `%(push)`, `%(worktreepath)`, `%(flag)`, `%(HEAD)`, because our objects don't have a refname.

The performance for `git cat-file --batch-all-objects --batch-check` on the Git repository itself with performance testing tool `hyperfine` changes from 669.4 ms ± 31.1 ms to 1.134 s ± 0.063 s.

The performance for `git cat-file --batch-all-objects --batch
>/dev/null` on the Git repository itself with performance testing

tool `time` change from "27.37s user 0.29s system 98% cpu 28.089 total" to "33.69s user 1.54s system 87% cpu 40.258 total".

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Hariom Verma <hariom18599@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 Documentation/git-cat-file.txt |   6 +
 builtin/cat-file.c             | 244 ++++++-------------------------
 t/t1006-cat-file.sh            | 252 +++++++++++++++++++++++++++++++++
 3 files changed, 305 insertions(+), 197 deletions(-)
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 4eb0421b3fd9..ef8ab952b2fa 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -226,6 +226,12 @@ newline. The available atoms are:
 	after that first run of whitespace (i.e., the "rest" of the
 	line) are output in place of the `%(rest)` atom.
 
+Note that most of the atoms in `for-each-ref --format` are now supported,
+such as `%(tree)`, `%(parent)`, `%(author)`, `%(tagger)`, `%(if)`,
+`%(then)`, `%(else)`, `%(end)`. But these atoms will be rejected:
+`%(refname)`, `%(symref)`, `%(upstream)`, `%(push)`, `%(worktreepath)`,
+`%(flag)`, `%(HEAD)`. See linkgit:git-for-each-ref[1].
+
 If no format is specified, the default format is `%(objectname)
 %(objecttype) %(objectsize)`.
 
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index cd84c39df968..0e7ad038e5fb 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -16,6 +16,7 @@
 #include "packfile.h"
 #include "object-store.h"
 #include "promisor-remote.h"
+#include "ref-filter.h"
 
 struct batch_options {
 	int enabled;
@@ -25,7 +26,7 @@ struct batch_options {
 	int all_objects;
 	int unordered;
 	int cmdmode; /* may be 'w' or 'c' for --filters or --textconv */
-	const char *format;
+	struct ref_format format;
 };
 
 static const char *force_path;
@@ -195,99 +196,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
 
 struct expand_data {
 	struct object_id oid;
-	enum object_type type;
-	unsigned long size;
-	off_t disk_size;
 	const char *rest;
-	struct object_id delta_base_oid;
-
-	/*
-	 * If mark_query is true, we do not expand anything, but rather
-	 * just mark the object_info with items we wish to query.
-	 */
-	int mark_query;
-
-	/*
-	 * Whether to split the input on whitespace before feeding it to
-	 * get_sha1; this is decided during the mark_query phase based on
-	 * whether we have a %(rest) token in our format.
-	 */
 	int split_on_whitespace;
-
-	/*
-	 * After a mark_query run, this object_info is set up to be
-	 * passed to oid_object_info_extended. It will point to the data
-	 * elements above, so you can retrieve the response from there.
-	 */
-	struct object_info info;
-
-	/*
-	 * This flag will be true if the requested batch format and options
-	 * don't require us to call oid_object_info, which can then be
-	 * optimized out.
-	 */
-	unsigned skip_object_info : 1;
 };
 
-static int is_atom(const char *atom, const char *s, int slen)
-{
-	int alen = strlen(atom);
-	return alen == slen && !memcmp(atom, s, alen);
-}
-
-static void expand_atom(struct strbuf *sb, const char *atom, int len,
-			void *vdata)
-{
-	struct expand_data *data = vdata;
-
-	if (is_atom("objectname", atom, len)) {
-		if (!data->mark_query)
-			strbuf_addstr(sb, oid_to_hex(&data->oid));
-	} else if (is_atom("objecttype", atom, len)) {
-		if (data->mark_query)
-			data->info.typep = &data->type;
-		else
-			strbuf_addstr(sb, type_name(data->type));
-	} else if (is_atom("objectsize", atom, len)) {
-		if (data->mark_query)
-			data->info.sizep = &data->size;
-		else
-			strbuf_addf(sb, "%"PRIuMAX , (uintmax_t)data->size);
-	} else if (is_atom("objectsize:disk", atom, len)) {
-		if (data->mark_query)
-			data->info.disk_sizep = &data->disk_size;
-		else
-			strbuf_addf(sb, "%"PRIuMAX, (uintmax_t)data->disk_size);
-	} else if (is_atom("rest", atom, len)) {
-		if (data->mark_query)
-			data->split_on_whitespace = 1;
-		else if (data->rest)
-			strbuf_addstr(sb, data->rest);
-	} else if (is_atom("deltabase", atom, len)) {
-		if (data->mark_query)
-			data->info.delta_base_oid = &data->delta_base_oid;
-		else
-			strbuf_addstr(sb,
-				      oid_to_hex(&data->delta_base_oid));
-	} else
-		die("unknown format element: %.*s", len, atom);
-}
-
-static size_t expand_format(struct strbuf *sb, const char *start, void *data)
-{
-	const char *end;
-
-	if (*start != '(')
-		return 0;
-	end = strchr(start + 1, ')');
-	if (!end)
-		die("format element '%s' does not end in ')'", start);
-
-	expand_atom(sb, start + 1, end - start - 1, data);
-
-	return end - start + 1;
-}
-
 static void batch_write(struct batch_options *opt, const void *data, int len)
 {
 	if (opt->buffer_output) {
@@ -297,87 +209,34 @@ static void batch_write(struct batch_options *opt, const void *data, int len)
 		write_or_die(1, data, len);
 }
 
-static void print_object_or_die(struct batch_options *opt, struct expand_data *data)
-{
-	const struct object_id *oid = &data->oid;
-
-	assert(data->info.typep);
-
-	if (data->type == OBJ_BLOB) {
-		if (opt->buffer_output)
-			fflush(stdout);
-		if (opt->cmdmode) {
-			char *contents;
-			unsigned long size;
-
-			if (!data->rest)
-				die("missing path for '%s'", oid_to_hex(oid));
-
-			if (opt->cmdmode == 'w') {
-				if (filter_object(data->rest, 0100644, oid,
-						  &contents, &size))
-					die("could not convert '%s' %s",
-					    oid_to_hex(oid), data->rest);
-			} else if (opt->cmdmode == 'c') {
-				enum object_type type;
-				if (!textconv_object(the_repository,
-						     data->rest, 0100644, oid,
-						     1, &contents, &size))
-					contents = read_object_file(oid,
-								    &type,
-								    &size);
-				if (!contents)
-					die("could not convert '%s' %s",
-					    oid_to_hex(oid), data->rest);
-			} else
-				BUG("invalid cmdmode: %c", opt->cmdmode);
-			batch_write(opt, contents, size);
-			free(contents);
-		} else {
-			stream_blob(oid);
-		}
-	}
-	else {
-		enum object_type type;
-		unsigned long size;
-		void *contents;
-
-		contents = read_object_file(oid, &type, &size);
-		if (!contents)
-			die("object %s disappeared", oid_to_hex(oid));
-		if (type != data->type)
-			die("object %s changed type!?", oid_to_hex(oid));
-		if (data->info.sizep && size != data->size)
-			die("object %s changed size!?", oid_to_hex(oid));
-
-		batch_write(opt, contents, size);
-		free(contents);
-	}
-}
 
 static void batch_object_write(const char *obj_name,
 			       struct strbuf *scratch,
 			       struct batch_options *opt,
 			       struct expand_data *data)
 {
-	if (!data->skip_object_info &&
-	    oid_object_info_extended(the_repository, &data->oid, &data->info,
-				     OBJECT_INFO_LOOKUP_REPLACE) < 0) {
-		printf("%s missing\n",
-		       obj_name ? obj_name : oid_to_hex(&data->oid));
-		fflush(stdout);
-		return;
-	}
+	int ret = 0;
+	struct strbuf err = STRBUF_INIT;
+	struct ref_array_item item = { data->oid, data->rest };
 
 	strbuf_reset(scratch);
-	strbuf_expand(scratch, opt->format, expand_format, data);
-	strbuf_addch(scratch, '\n');
-	batch_write(opt, scratch->buf, scratch->len);
 
-	if (opt->print_contents) {
-		print_object_or_die(opt, data);
-		batch_write(opt, "\n", 1);
+	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+	if (ret < 0) {
+		die("%s\n", err.buf);
+	} if (ret) {
+		/* ret > 0 means when the object corresponding to oid
+		 * cannot be found in format_ref_array_item(), we only print
+		 * the error message.
+		 */
+		printf("%s\n", err.buf);
+		fflush(stdout);
+	} else {
+		strbuf_addch(scratch, '\n');
+		batch_write(opt, scratch->buf, scratch->len);
 	}
+	free_ref_array_item_value(&item);
+	strbuf_release(&err);
 }
 
 static void batch_one_object(const char *obj_name,
@@ -495,42 +354,34 @@ static int batch_unordered_packed(const struct object_id *oid,
 	return batch_unordered_object(oid, data);
 }
 
-static int batch_objects(struct batch_options *batch)
+static const char * const cat_file_usage[] = {
+	N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) [--path=<path>] <object>"),
+	N_("git cat-file (--batch[=<format>] | --batch-check[=<format>]) [--follow-symlinks] [--textconv | --filters]"),
+	NULL
+};
+
+static int batch_objects(struct batch_options *batch, const struct option *options)
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
+	struct strbuf format = STRBUF_INIT;
 	struct expand_data data;
 	int save_warning;
 	int retval = 0;
 
-	if (!batch->format)
-		batch->format = "%(objectname) %(objecttype) %(objectsize)";
-
-	/*
-	 * Expand once with our special mark_query flag, which will prime the
-	 * object_info to be handed to oid_object_info_extended for each
-	 * object.
-	 */
 	memset(&data, 0, sizeof(data));
-	data.mark_query = 1;
-	strbuf_expand(&output, batch->format, expand_format, &data);
-	data.mark_query = 0;
-	strbuf_release(&output);
-	if (batch->cmdmode)
-		data.split_on_whitespace = 1;
-
-	if (batch->all_objects) {
-		struct object_info empty = OBJECT_INFO_INIT;
-		if (!memcmp(&data.info, &empty, sizeof(empty)))
-			data.skip_object_info = 1;
-	}
-
-	/*
-	 * If we are printing out the object, then always fill in the type,
-	 * since we will want to decide whether or not to stream.
-	 */
+	if (batch->format.format)
+		strbuf_addstr(&format, batch->format.format);
+	else
+		strbuf_addstr(&format, "%(objectname) %(objecttype) %(objectsize)");
 	if (batch->print_contents)
-		data.info.typep = &data.type;
+		strbuf_addstr(&format, "\n%(raw)");
+	batch->format.format = format.buf;
+	if (verify_ref_format(&batch->format))
+		usage_with_options(cat_file_usage, options);
+
+	if (batch->cmdmode || batch->format.use_rest)
+		data.split_on_whitespace = 1;
 
 	if (batch->all_objects) {
 		struct object_cb_data cb;
@@ -563,6 +414,7 @@ static int batch_objects(struct batch_options *batch)
 			oid_array_clear(&sa);
 		}
 
+		strbuf_release(&format);
 		strbuf_release(&output);
 		return 0;
 	}
@@ -595,18 +447,13 @@ static int batch_objects(struct batch_options *batch)
 		batch_one_object(input.buf, &output, batch, &data);
 	}
 
+	strbuf_release(&format);
 	strbuf_release(&input);
 	strbuf_release(&output);
 	warn_on_object_refname_ambiguity = save_warning;
 	return retval;
 }
 
-static const char * const cat_file_usage[] = {
-	N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) [--path=<path>] <object>"),
-	N_("git cat-file (--batch[=<format>] | --batch-check[=<format>]) [--follow-symlinks] [--textconv | --filters]"),
-	NULL
-};
-
 static int git_cat_file_config(const char *var, const char *value, void *cb)
 {
 	if (userdiff_config(var, value) < 0)
@@ -629,7 +476,7 @@ static int batch_option_callback(const struct option *opt,
 
 	bo->enabled = 1;
 	bo->print_contents = !strcmp(opt->long_name, "batch");
-	bo->format = arg;
+	bo->format.format = arg;
 
 	return 0;
 }
@@ -638,7 +485,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
 {
 	int opt = 0;
 	const char *exp_type = NULL, *obj_name = NULL;
-	struct batch_options batch = {0};
+	struct batch_options batch = {
+		.format = REF_FORMAT_INIT
+	};
 	int unknown_type = 0;
 
 	const struct option options[] = {
@@ -677,6 +526,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
 	git_config(git_cat_file_config, NULL);
 
 	batch.buffer_output = -1;
+	batch.format.cat_file_mode = 1;
 	argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
 
 	if (opt) {
@@ -720,7 +570,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
 		batch.buffer_output = batch.all_objects;
 
 	if (batch.enabled)
-		return batch_objects(&batch);
+		return batch_objects(&batch, options);
 
 	if (unknown_type && opt != 't' && opt != 's')
 		die("git cat-file --allow-unknown-type: use with -s or -t");
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 5d2dc99b74ad..69eb627774d9 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -586,4 +586,256 @@ test_expect_success 'cat-file --unordered works' '
 	test_cmp expect actual
 '
 
+. "$TEST_DIRECTORY"/lib-gpg.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+
+test_expect_success 'cat-file --batch|--batch-check setup' '
+	echo 1>blob1 &&
+	printf "a\0b\0\c" >blob2 &&
+	git add blob1 blob2 &&
+	git commit -m "Commit Message" &&
+	git branch -M main &&
+	git tag -a -m "v0.0.0" testtag &&
+	git update-ref refs/myblobs/blob1 HEAD:blob1 &&
+	git update-ref refs/myblobs/blob2 HEAD:blob2 &&
+	git update-ref refs/mytrees/tree1 HEAD^{tree}
+'
+
+batch_test_atom() {
+	if test "$3" = "fail"
+	then
+		test_expect_${4:-success} $PREREQ "basic atom: $1 $2 must fail" "
+			test_must_fail git cat-file --batch-check='$2' >bad <<-EOF
+			$1
+			EOF
+		"
+	else
+		test_expect_${4:-success} $PREREQ "basic atom: $1 $2" "
+			git for-each-ref --format='$2' $1 >expected &&
+			git cat-file --batch-check='$2' >actual <<-EOF &&
+			$1
+			EOF
+			sanitize_pgp <actual >actual.clean &&
+			cmp expected actual.clean
+		"
+	fi
+}
+
+batch_test_atom refs/heads/main '%(refname)' fail
+batch_test_atom refs/heads/main '%(refname:)' fail
+batch_test_atom refs/heads/main '%(refname:short)' fail
+batch_test_atom refs/heads/main '%(refname:lstrip=1)' fail
+batch_test_atom refs/heads/main '%(refname:lstrip=2)' fail
+batch_test_atom refs/heads/main '%(refname:lstrip=-1)' fail
+batch_test_atom refs/heads/main '%(refname:lstrip=-2)' fail
+batch_test_atom refs/heads/main '%(refname:rstrip=1)' fail
+batch_test_atom refs/heads/main '%(refname:rstrip=2)' fail
+batch_test_atom refs/heads/main '%(refname:rstrip=-1)' fail
+batch_test_atom refs/heads/main '%(refname:rstrip=-2)' fail
+batch_test_atom refs/heads/main '%(refname:strip=1)' fail
+batch_test_atom refs/heads/main '%(refname:strip=2)' fail
+batch_test_atom refs/heads/main '%(refname:strip=-1)' fail
+batch_test_atom refs/heads/main '%(refname:strip=-2)' fail
+batch_test_atom refs/heads/main '%(upstream)' fail
+batch_test_atom refs/heads/main '%(upstream:short)' fail
+batch_test_atom refs/heads/main '%(upstream:lstrip=2)' fail
+batch_test_atom refs/heads/main '%(upstream:lstrip=-2)' fail
+batch_test_atom refs/heads/main '%(upstream:rstrip=2)' fail
+batch_test_atom refs/heads/main '%(upstream:rstrip=-2)' fail
+batch_test_atom refs/heads/main '%(upstream:strip=2)' fail
+batch_test_atom refs/heads/main '%(upstream:strip=-2)' fail
+batch_test_atom refs/heads/main '%(push)' fail
+batch_test_atom refs/heads/main '%(push:short)' fail
+batch_test_atom refs/heads/main '%(push:lstrip=1)' fail
+batch_test_atom refs/heads/main '%(push:lstrip=-1)' fail
+batch_test_atom refs/heads/main '%(push:rstrip=1)' fail
+batch_test_atom refs/heads/main '%(push:rstrip=-1)' fail
+batch_test_atom refs/heads/main '%(push:strip=1)' fail
+batch_test_atom refs/heads/main '%(push:strip=-1)' fail
+batch_test_atom refs/heads/main '%(objecttype)'
+batch_test_atom refs/heads/main '%(objectsize)'
+batch_test_atom refs/heads/main '%(objectsize:disk)'
+batch_test_atom refs/heads/main '%(deltabase)'
+batch_test_atom refs/heads/main '%(objectname)'
+batch_test_atom refs/heads/main '%(objectname:short)'
+batch_test_atom refs/heads/main '%(objectname:short=1)'
+batch_test_atom refs/heads/main '%(objectname:short=10)'
+batch_test_atom refs/heads/main '%(tree)'
+batch_test_atom refs/heads/main '%(tree:short)'
+batch_test_atom refs/heads/main '%(tree:short=1)'
+batch_test_atom refs/heads/main '%(tree:short=10)'
+batch_test_atom refs/heads/main '%(parent)'
+batch_test_atom refs/heads/main '%(parent:short)'
+batch_test_atom refs/heads/main '%(parent:short=1)'
+batch_test_atom refs/heads/main '%(parent:short=10)'
+batch_test_atom refs/heads/main '%(numparent)'
+batch_test_atom refs/heads/main '%(object)'
+batch_test_atom refs/heads/main '%(type)'
+batch_test_atom refs/heads/main '%(raw)'
+batch_test_atom refs/heads/main '%(*objectname)'
+batch_test_atom refs/heads/main '%(*objecttype)'
+batch_test_atom refs/heads/main '%(author)'
+batch_test_atom refs/heads/main '%(authorname)'
+batch_test_atom refs/heads/main '%(authoremail)'
+batch_test_atom refs/heads/main '%(authoremail:trim)'
+batch_test_atom refs/heads/main '%(authoremail:localpart)'
+batch_test_atom refs/heads/main '%(authordate)'
+batch_test_atom refs/heads/main '%(committer)'
+batch_test_atom refs/heads/main '%(committername)'
+batch_test_atom refs/heads/main '%(committeremail)'
+batch_test_atom refs/heads/main '%(committeremail:trim)'
+batch_test_atom refs/heads/main '%(committeremail:localpart)'
+batch_test_atom refs/heads/main '%(committerdate)'
+batch_test_atom refs/heads/main '%(tag)'
+batch_test_atom refs/heads/main '%(tagger)'
+batch_test_atom refs/heads/main '%(taggername)'
+batch_test_atom refs/heads/main '%(taggeremail)'
+batch_test_atom refs/heads/main '%(taggeremail:trim)'
+batch_test_atom refs/heads/main '%(taggeremail:localpart)'
+batch_test_atom refs/heads/main '%(taggerdate)'
+batch_test_atom refs/heads/main '%(creator)'
+batch_test_atom refs/heads/main '%(creatordate)'
+batch_test_atom refs/heads/main '%(subject)'
+batch_test_atom refs/heads/main '%(subject:sanitize)'
+batch_test_atom refs/heads/main '%(contents:subject)'
+batch_test_atom refs/heads/main '%(body)'
+batch_test_atom refs/heads/main '%(contents:body)'
+batch_test_atom refs/heads/main '%(contents:signature)'
+batch_test_atom refs/heads/main '%(contents)'
+batch_test_atom refs/heads/main '%(HEAD)' fail
+batch_test_atom refs/heads/main '%(upstream:track)' fail
+batch_test_atom refs/heads/main '%(upstream:trackshort)' fail
+batch_test_atom refs/heads/main '%(upstream:track,nobracket)' fail
+batch_test_atom refs/heads/main '%(upstream:nobracket,track)' fail
+batch_test_atom refs/heads/main '%(push:track)' fail
+batch_test_atom refs/heads/main '%(push:trackshort)' fail
+batch_test_atom refs/heads/main '%(worktreepath)' fail
+batch_test_atom refs/heads/main '%(symref)' fail
+batch_test_atom refs/heads/main '%(flag)' fail
+
+batch_test_atom refs/tags/testtag '%(refname)' fail
+batch_test_atom refs/tags/testtag '%(refname:short)' fail
+batch_test_atom refs/tags/testtag '%(upstream)' fail
+batch_test_atom refs/tags/testtag '%(push)' fail
+batch_test_atom refs/tags/testtag '%(objecttype)'
+batch_test_atom refs/tags/testtag '%(objectsize)'
+batch_test_atom refs/tags/testtag '%(objectsize:disk)'
+batch_test_atom refs/tags/testtag '%(*objectsize:disk)'
+batch_test_atom refs/tags/testtag '%(deltabase)'
+batch_test_atom refs/tags/testtag '%(*deltabase)'
+batch_test_atom refs/tags/testtag '%(objectname)'
+batch_test_atom refs/tags/testtag '%(objectname:short)'
+batch_test_atom refs/tags/testtag '%(tree)'
+batch_test_atom refs/tags/testtag '%(tree:short)'
+batch_test_atom refs/tags/testtag '%(tree:short=1)'
+batch_test_atom refs/tags/testtag '%(tree:short=10)'
+batch_test_atom refs/tags/testtag '%(parent)'
+batch_test_atom refs/tags/testtag '%(parent:short)'
+batch_test_atom refs/tags/testtag '%(parent:short=1)'
+batch_test_atom refs/tags/testtag '%(parent:short=10)'
+batch_test_atom refs/tags/testtag '%(numparent)'
+batch_test_atom refs/tags/testtag '%(object)'
+batch_test_atom refs/tags/testtag '%(type)'
+batch_test_atom refs/tags/testtag '%(*objectname)'
+batch_test_atom refs/tags/testtag '%(*objecttype)'
+batch_test_atom refs/tags/testtag '%(author)'
+batch_test_atom refs/tags/testtag '%(authorname)'
+batch_test_atom refs/tags/testtag '%(authoremail)'
+batch_test_atom refs/tags/testtag '%(authoremail:trim)'
+batch_test_atom refs/tags/testtag '%(authoremail:localpart)'
+batch_test_atom refs/tags/testtag '%(authordate)'
+batch_test_atom refs/tags/testtag '%(committer)'
+batch_test_atom refs/tags/testtag '%(committername)'
+batch_test_atom refs/tags/testtag '%(committeremail)'
+batch_test_atom refs/tags/testtag '%(committeremail:trim)'
+batch_test_atom refs/tags/testtag '%(committeremail:localpart)'
+batch_test_atom refs/tags/testtag '%(committerdate)'
+batch_test_atom refs/tags/testtag '%(tag)'
+batch_test_atom refs/tags/testtag '%(tagger)'
+batch_test_atom refs/tags/testtag '%(taggername)'
+batch_test_atom refs/tags/testtag '%(taggeremail)'
+batch_test_atom refs/tags/testtag '%(taggeremail:trim)'
+batch_test_atom refs/tags/testtag '%(taggeremail:localpart)'
+batch_test_atom refs/tags/testtag '%(taggerdate)'
+batch_test_atom refs/tags/testtag '%(creator)'
+batch_test_atom refs/tags/testtag '%(creatordate)'
+batch_test_atom refs/tags/testtag '%(subject)'
+batch_test_atom refs/tags/testtag '%(subject:sanitize)'
+batch_test_atom refs/tags/testtag '%(contents:subject)'
+batch_test_atom refs/tags/testtag '%(body)'
+batch_test_atom refs/tags/testtag '%(contents:body)'
+batch_test_atom refs/tags/testtag '%(contents:signature)'
+batch_test_atom refs/tags/testtag '%(contents)'
+batch_test_atom refs/tags/testtag '%(HEAD)' fail
+
+batch_test_atom refs/myblobs/blob1 '%(refname)' fail
+batch_test_atom refs/myblobs/blob1 '%(upstream)' fail
+batch_test_atom refs/myblobs/blob1 '%(push)' fail
+batch_test_atom refs/myblobs/blob1 '%(HEAD)' fail
+
+batch_test_atom refs/myblobs/blob1 '%(objectname)'
+batch_test_atom refs/myblobs/blob1 '%(objecttype)'
+batch_test_atom refs/myblobs/blob1 '%(objectsize)'
+batch_test_atom refs/myblobs/blob1 '%(objectsize:disk)'
+batch_test_atom refs/myblobs/blob1 '%(deltabase)'
+
+batch_test_atom refs/myblobs/blob1 '%(contents)'
+batch_test_atom refs/myblobs/blob2 '%(contents)'
+
+batch_test_atom refs/myblobs/blob1 '%(raw)'
+batch_test_atom refs/myblobs/blob2 '%(raw)'
+batch_test_atom refs/mytrees/tree1 '%(raw)'
+
+batch_test_atom refs/myblobs/blob1 '%(raw:size)'
+batch_test_atom refs/myblobs/blob2 '%(raw:size)'
+batch_test_atom refs/mytrees/tree1 '%(raw:size)'
+
+batch_test_atom refs/myblobs/blob1 '%(if:equals=blob)%(objecttype)%(then)commit%(else)not commit%(end)'
+batch_test_atom refs/myblobs/blob2 '%(if:equals=blob)%(objecttype)%(then)commit%(else)not commit%(end)'
+batch_test_atom refs/mytrees/tree1 '%(if:equals=tree)%(objecttype)%(then)tree%(else)not tree%(end)'
+
+batch_test_atom refs/heads/main '%(align:60) objectname is %(objectname)%(end)|%(objectname)'
+batch_test_atom refs/heads/main '%(align:left,60) objectname is %(objectname)%(end)|%(objectname)'
+batch_test_atom refs/heads/main '%(align:middle,60) objectname is %(objectname)%(end)|%(objectname)'
+batch_test_atom refs/heads/main '%(align:60,right) objectname is %(objectname)%(end)|%(objectname)'
+
+batch_test_atom refs/heads/main 'VALID'
+batch_test_atom refs/heads/main '%(INVALID)' fail
+batch_test_atom refs/heads/main '%(authordate:INVALID)' fail
+
+test_expect_success '%(rest) works with both a branch and a tag' '
+	cat >expected <<-EOF &&
+	123 commit 123
+	456 tag 456
+	EOF
+	git cat-file --batch-check="%(rest) %(objecttype) %(rest)" >actual <<-EOF &&
+	refs/heads/main 123
+	refs/tags/testtag 456
+	EOF
+	test_cmp expected actual
+'
+
+batch_test_atom refs/heads/main '%(objectname) %(objecttype) %(objectsize)
+%(raw)'
+batch_test_atom refs/tags/testtag '%(objectname) %(objecttype) %(objectsize)
+%(raw)'
+batch_test_atom refs/myblobs/blob1 '%(objectname) %(objecttype) %(objectsize)
+%(raw)'
+batch_test_atom refs/myblobs/blob2 '%(objectname) %(objecttype) %(objectsize)
+%(raw)'
+
+
+test_expect_success 'cat-file --batch equals to --batch-check with atoms' '
+	git cat-file --batch-check="%(objectname) %(objecttype) %(objectsize)
+%(raw)" >expected <<-EOF &&
+	refs/heads/main
+	refs/tags/testtag
+	EOF
+	git cat-file --batch >actual <<-EOF &&
+	refs/heads/main
+	refs/tags/testtag
+	EOF
+	cmp expected actual
+'
+
 test_done
-- 
gitgitgadget
Previous: Bagas SanjayaNext: ZheNing Hu via GitGitGadget
Message 74 of 121 in “[GSOC][RFC] cat-file: reuse ref-filter logic”
  1. 0/8 [GSOC][RFC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 12, 2021
  2. 3/8 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()ZheNing Hu via GitGitGadget, Jun 12, 2021
  3. 6/8 [GSOC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 12, 2021
  4. 8/8 [GSOC] cat-file: re-implement --textconv, --filters optionsZheNing Hu via GitGitGadget, Jun 12, 2021
  5. Ævar Arnfjörð BjarmasonJun 17, 2021
  6. ZheNing HuJun 17, 2021
  7. 4/8 [GSOC] ref-filter: add %(rest) atomZheNing Hu via GitGitGadget, Jun 12, 2021
  8. 2/8 [GSOC] ref-filter: add %(raw) atomZheNing Hu via GitGitGadget, Jun 12, 2021
  9. Ævar Arnfjörð BjarmasonJun 17, 2021
  10. Junio C HamanoJun 17, 2021
  11. ZheNing HuJun 17, 2021
  12. Ævar Arnfjörð BjarmasonJun 17, 2021
  13. ZheNing HuJun 17, 2021
  14. Ævar Arnfjörð BjarmasonJun 18, 2021
  15. Christian CouderJun 18, 2021
  16. 1/8 [GSOC] ref-filter: add obj-type check in grab contentsZheNing Hu via GitGitGadget, Jun 12, 2021
  17. Ævar Arnfjörð BjarmasonJun 17, 2021
  18. Junio C HamanoJun 17, 2021
  19. 5/8 [GSOC] ref-filter: teach get_object() return useful valueZheNing Hu via GitGitGadget, Jun 12, 2021
  20. Christian CouderJun 12, 2021
  21. ZheNing HuJun 13, 2021
  22. Ævar Arnfjörð BjarmasonJun 17, 2021
  23. ZheNing HuJun 17, 2021
  24. 7/8 [GSOC] cat-file: reuse err buf in batch_objet_write()ZheNing Hu via GitGitGadget, Jun 12, 2021
  25. Ævar Arnfjörð BjarmasonJun 17, 2021
  26. ZheNing HuJun 17, 2021
  27. 0/9 [GSOC][RFC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 15, 2021
  28. 1/9 [GSOC] ref-filter: add obj-type check in grab contentsZheNing Hu via GitGitGadget, Jun 15, 2021
  29. 2/9 [GSOC] ref-filter: add %(raw) atomZheNing Hu via GitGitGadget, Jun 15, 2021
  30. 4/9 [GSOC] ref-filter: add %(rest) atomZheNing Hu via GitGitGadget, Jun 15, 2021
  31. 3/9 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()ZheNing Hu via GitGitGadget, Jun 15, 2021
  32. 6/9 [GSOC] ref-filter: introduce free_array_item_internal() functionZheNing Hu via GitGitGadget, Jun 15, 2021
  33. Junio C HamanoJun 16, 2021
  34. ZheNing HuJun 17, 2021
  35. 5/9 [GSOC] ref-filter: teach get_object() return useful valueZheNing Hu via GitGitGadget, Jun 15, 2021
  36. Junio C HamanoJun 16, 2021
  37. ZheNing HuJun 17, 2021
  38. 7/9 [GSOC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 15, 2021
  39. 8/9 [GSOC] cat-file: reuse err buf in batch_objet_write()ZheNing Hu via GitGitGadget, Jun 15, 2021
  40. 9/9 [GSOC] cat-file: re-implement --textconv, --filters optionsZheNing Hu via GitGitGadget, Jun 15, 2021
  41. Junio C HamanoJun 16, 2021
  42. ZheNing HuJun 17, 2021
  43. Ævar Arnfjörð BjarmasonJun 17, 2021
  44. ZheNing HuJun 17, 2021
  45. 00/10 [GSOC][RFC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 19, 2021
  46. 01/10 [GSOC] ref-filter: add obj-type check in grab contentsZheNing Hu via GitGitGadget, Jun 19, 2021
  47. 04/10 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()ZheNing Hu via GitGitGadget, Jun 19, 2021
  48. 02/10 [GSOC] ref-filter: add %(raw) atomZheNing Hu via GitGitGadget, Jun 19, 2021
  49. 07/10 [GSOC] ref-filter: introduce free_ref_array_item_value() functionZheNing Hu via GitGitGadget, Jun 19, 2021
  50. 05/10 [GSOC] ref-filter: add %(rest) atomZheNing Hu via GitGitGadget, Jun 19, 2021
  51. 06/10 [GSOC] ref-filter: pass get_object() return value to their callersZheNing Hu via GitGitGadget, Jun 19, 2021
  52. 09/10 [GSOC] cat-file: reuse err buf in batch_object_write()ZheNing Hu via GitGitGadget, Jun 19, 2021
  53. 08/10 [GSOC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 19, 2021
  54. Christian CouderJun 21, 2021
  55. ZheNing HuJun 21, 2021
  56. 03/10 [GSOC] ref-filter: --format=%(raw) re-support --perlZheNing Hu via GitGitGadget, Jun 19, 2021
  57. 10/10 [GSOC] cat-file: re-implement --textconv, --filters optionsZheNing Hu via GitGitGadget, Jun 19, 2021
  58. 00/14 [GSOC][RFC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 22, 2021
  59. 01/14 [GSOC] ref-filter: add obj-type check in grab contentsZheNing Hu via GitGitGadget, Jun 22, 2021
  60. 03/14 [GSOC] ref-filter: --format=%(raw) re-support --perlZheNing Hu via GitGitGadget, Jun 22, 2021
  61. 02/14 [GSOC] ref-filter: add %(raw) atomZheNing Hu via GitGitGadget, Jun 22, 2021
  62. Bagas SanjayaJun 24, 2021
  63. ZheNing HuJun 24, 2021
  64. 04/14 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()ZheNing Hu via GitGitGadget, Jun 22, 2021
  65. 06/14 [GSOC] ref-filter: pass get_object() return value to their callersZheNing Hu via GitGitGadget, Jun 22, 2021
  66. Bagas SanjayaJun 24, 2021
  67. 05/14 [GSOC] ref-filter: add %(rest) atomZheNing Hu via GitGitGadget, Jun 22, 2021
  68. 08/14 [GSOC] ref-filter: add cat_file_mode in struct ref_formatZheNing Hu via GitGitGadget, Jun 22, 2021
  69. 07/14 [GSOC] ref-filter: introduce free_ref_array_item_value() functionZheNing Hu via GitGitGadget, Jun 22, 2021
  70. 09/14 [GSOC] ref-filter: modify the error message and value in get_objectZheNing Hu via GitGitGadget, Jun 22, 2021
  71. 10/14 [GSOC] cat-file: add has_object_file() checkZheNing Hu via GitGitGadget, Jun 22, 2021
  72. 11/14 [GSOC] cat-file: change batch_objects parameter nameZheNing Hu via GitGitGadget, Jun 22, 2021
  73. Bagas SanjayaJun 24, 2021
  74. 12/14 [GSOC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 22, 2021
  75. 13/14 [GSOC] cat-file: reuse err buf in batch_object_write()ZheNing Hu via GitGitGadget, Jun 22, 2021
  76. 14/14 [GSOC] cat-file: re-implement --textconv, --filters optionsZheNing Hu via GitGitGadget, Jun 22, 2021
  77. 00/15 [GSOC][RFC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 25, 2021
  78. 02/15 [GSOC] ref-filter: add %(raw) atomZheNing Hu via GitGitGadget, Jun 25, 2021
  79. Bagas SanjayaJun 26, 2021
  80. ZheNing HuJun 27, 2021
  81. 01/15 [GSOC] ref-filter: add obj-type check in grab contentsZheNing Hu via GitGitGadget, Jun 25, 2021
  82. 03/15 [GSOC] ref-filter: --format=%(raw) re-support --perlZheNing Hu via GitGitGadget, Jun 25, 2021
  83. 05/15 [GSOC] ref-filter: add %(rest) atomZheNing Hu via GitGitGadget, Jun 25, 2021
  84. 04/15 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()ZheNing Hu via GitGitGadget, Jun 25, 2021
  85. 06/15 [GSOC] ref-filter: pass get_object() return value to their callersZheNing Hu via GitGitGadget, Jun 25, 2021
  86. 08/15 [GSOC] ref-filter: add cat_file_mode in struct ref_formatZheNing Hu via GitGitGadget, Jun 25, 2021
  87. 07/15 [GSOC] ref-filter: introduce free_ref_array_item_value() functionZheNing Hu via GitGitGadget, Jun 25, 2021
  88. 10/15 [GSOC] cat-file: add has_object_file() checkZheNing Hu via GitGitGadget, Jun 25, 2021
  89. 09/15 [GSOC] ref-filter: modify the error message and value in get_objectZheNing Hu via GitGitGadget, Jun 25, 2021
  90. 11/15 [GSOC] cat-file: change batch_objects parameter nameZheNing Hu via GitGitGadget, Jun 25, 2021
  91. 12/15 [GSOC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 25, 2021
  92. Hariom vermaJun 26, 2021
  93. ZheNing HuJun 27, 2021
  94. Hariom vermaJun 26, 2021
  95. ZheNing HuJun 27, 2021
  96. 13/15 [GSOC] cat-file: reuse err buf in batch_object_write()ZheNing Hu via GitGitGadget, Jun 25, 2021
  97. 14/15 [GSOC] cat-file: re-implement --textconv, --filters optionsZheNing Hu via GitGitGadget, Jun 25, 2021
  98. 15/15 [GSOC] ref-filter: remove grab_oid() functionZheNing Hu via GitGitGadget, Jun 25, 2021
  99. 00/15 [GSOC][RFC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 27, 2021
  100. 02/15 [GSOC] ref-filter: add %(raw) atomZheNing Hu via GitGitGadget, Jun 27, 2021
  101. Bagas SanjayaJun 28, 2021
  102. 01/15 [GSOC] ref-filter: add obj-type check in grab contentsZheNing Hu via GitGitGadget, Jun 27, 2021
  103. 03/15 [GSOC] ref-filter: --format=%(raw) re-support --perlZheNing Hu via GitGitGadget, Jun 27, 2021
  104. 07/15 [GSOC] ref-filter: introduce free_ref_array_item_value() functionZheNing Hu via GitGitGadget, Jun 27, 2021
  105. 04/15 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()ZheNing Hu via GitGitGadget, Jun 27, 2021
  106. 08/15 [GSOC] ref-filter: add cat_file_mode in struct ref_formatZheNing Hu via GitGitGadget, Jun 27, 2021
  107. 05/15 [GSOC] ref-filter: add %(rest) atomZheNing Hu via GitGitGadget, Jun 27, 2021
  108. 06/15 [GSOC] ref-filter: pass get_object() return value to their callersZheNing Hu via GitGitGadget, Jun 27, 2021
  109. 09/15 [GSOC] ref-filter: modify the error message and value in get_objectZheNing Hu via GitGitGadget, Jun 27, 2021
  110. 12/15 [GSOC] cat-file: reuse ref-filter logicZheNing Hu via GitGitGadget, Jun 27, 2021
  111. Hariom vermaJun 28, 2021
  112. ZheNing HuJun 28, 2021
  113. 11/15 [GSOC] cat-file: change batch_objects parameter nameZheNing Hu via GitGitGadget, Jun 27, 2021
  114. 13/15 [GSOC] cat-file: reuse err buf in batch_object_write()ZheNing Hu via GitGitGadget, Jun 27, 2021
  115. 10/15 [GSOC] cat-file: add has_object_file() checkZheNing Hu via GitGitGadget, Jun 27, 2021
  116. 15/15 [GSOC] ref-filter: remove grab_oid() functionZheNing Hu via GitGitGadget, Jun 27, 2021
  117. 14/15 [GSOC] cat-file: re-implement --textconv, --filters optionsZheNing Hu via GitGitGadget, Jun 27, 2021
  118. Junio C HamanoJun 30, 2021
  119. ZheNing HuJul 1, 2021
  120. Junio C HamanoJul 1, 2021
  121. ZheNing HuJul 9, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.