Skip to contentSkip to conversation

cat-file: reuse ref-filter logic

RFC PATCH Superseded ZheNing Hu via GitGitGadget 7 participants 121 messages Jun 12, 2021 – Jul 9, 2021 Documentation

lore
Share & tools
lore
#0 lore raw

cover cat-file: reuse ref-filter logic

This patch series make cat-file reuse ref-filter logic, which based on 5a5b5f78 ([GSOC] ref-filter: add %(rest) atom)

1. Modified the logic of cat-file --batch, use verify_ref_format() and format_ref_array_item() to get object data.
2. Re-implement --textconv, --filters.

Now cat-file can support most ref-filter atoms, like %(tree), %(parent), %(if)...

There is still an unresolved issue: performance overhead is very large, so that when we use:

git cat-file --batch --batch-all-objects >/dev/null

on git.git, it may fail.

ZheNing Hu (8):
[GSOC] ref-filter: add obj-type check in grab contents [GSOC] ref-filter: add %(raw) atom
[GSOC] ref-filter: use non-const ref_format in *_atom_parser() [GSOC] ref-filter: add %(rest) atom
[GSOC] ref-filter: teach get_object() return useful value [GSOC] cat-file: reuse ref-filter logic
[GSOC] cat-file: reuse err buf in batch_objet_write() [GSOC] cat-file: re-implement --textconv, --filters options

 Documentation/git-cat-file.txt     |   6 +
 Documentation/git-for-each-ref.txt |   9 +
 builtin/cat-file.c                 | 267 ++++++------------------
 builtin/tag.c                      |   2 +-
 ref-filter.c                       | 320 +++++++++++++++++++++++------
 ref-filter.h                       |  13 +-
 t/t1006-cat-file.sh                | 252 +++++++++++++++++++++++
 t/t3203-branch-output.sh           |   4 +
 t/t6300-for-each-ref.sh            | 211 +++++++++++++++++++
 t/t6301-for-each-ref-errors.sh     |   2 +-
 t/t7004-tag.sh                     |   4 +
 t/t7030-verify-tag.sh              |   4 +
 12 files changed, 818 insertions(+), 276 deletions(-)

base-commit: 1197f1a46360d3ae96bd9c15908a3a6f8e562207
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-980%2Fadlternative%2Fcat-file-batch-refactor-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-980/adlternative/cat-file-batch-refactor-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/980

Signature · 1 line
gitgitgadget
#1 lore raw

PATCH 1/8 ref-filter: add obj-type check in grab contents

From: ZheNing Hu <[email protected]>

Only tag and commit objects use grab_sub_body_contents() to grab object contents in the current codebase. We want to teach the function to also handle blobs and trees to get their raw data, without parsing a blob (whose contents looks like a commit or a tag) incorrectly as a commit or a tag.

Skip the block of code that is specific to handling commits and tags early when the given object is of a wrong type to help later addition to handle other types of objects in this function.

---
 ref-filter.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)
ref-filter.c +15 -9
index 4db0e40ff4c6..5cee6512fbaf 100644
@@ -1356,11 +1356,12 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
 }
 
 /* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
 {
 	int i;
 	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
 	size_t sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+	void *buf = data->content;
 
 	for (i = 0; i < used_atom_cnt; i++) {
 		struct used_atom *atom = &used_atom[i];
@@ -1371,10 +1372,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
 			continue;
 		if (deref)
 			name++;
-		if (strcmp(name, "body") &&
-		    !starts_with(name, "subject") &&
-		    !starts_with(name, "trailers") &&
-		    !starts_with(name, "contents"))
+
+		if ((data->type != OBJ_TAG &&
+		     data->type != OBJ_COMMIT) ||
+		    (strcmp(name, "body") &&
+		     !starts_with(name, "subject") &&
+		     !starts_with(name, "trailers") &&
+		     !starts_with(name, "contents")))
 			continue;
 		if (!subpos)
 			find_subpos(buf,
@@ -1438,17 +1442,19 @@ static void fill_missing_values(struct atom_value *val)
  * pointed at by the ref itself; otherwise it is the object the
  * ref (which is a tag) refers to.
  */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
+static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
 {
+	void *buf = data->content;
+
 	switch (obj->type) {
 	case OBJ_TAG:
 		grab_tag_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("tagger", val, deref, buf);
 		break;
 	case OBJ_COMMIT:
 		grab_commit_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("author", val, deref, buf);
 		grab_person("committer", val, deref, buf);
 		break;
@@ -1678,7 +1684,7 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 			return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
 					       oid_to_hex(&oi->oid), ref->refname);
 		}
-		grab_values(ref->value, deref, *obj, oi->content);
+		grab_values(ref->value, deref, *obj, oi);
 	}
 
 	grab_common_values(ref->value, deref, oi);
Signature · 1 line
gitgitgadget
#2 lore raw

Re: [PATCH 1/8] [GSOC] ref-filter: add obj-type check in grab contents

Quoted 43 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Only tag and commit objects use grab_sub_body_contents() to grab object contents in the current codebase. We want to teach the function to also handle blobs and trees to get their raw data, without parsing a blob (whose contents looks like a commit or a tag) incorrectly as a commit or a tag.

Skip the block of code that is specific to handling commits and tags early when the given object is of a wrong type to help later addition to handle other types of objects in this function.

---
 ref-filter.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)
ref-filter.c +2 -2
index 4db0e40ff4c6..5cee6512fbaf 100644
@@ -1356,11 +1356,12 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
 }
 
 /* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
 {
 	int i;
 	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
 	size_t sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+	void *buf = data->content;
 
 	for (i = 0; i < used_atom_cnt; i++) {
 		struct used_atom *atom = &used_atom[i];
@@ -1371,10 +1372,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
 			continue;
 		if (deref)
 			name++;
-		if (strcmp(name, "body") &&

ref-filter.c static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf) · line 1381

Quoted 10 lines
ref-filter.c +7 -3
-		    !starts_with(name, "subject") &&
-		    !starts_with(name, "trailers") &&
-		    !starts_with(name, "contents"))
+
+		if ((data->type != OBJ_TAG &&
+		     data->type != OBJ_COMMIT) ||
+		    (strcmp(name, "body") &&
+		     !starts_with(name, "subject") &&
+		     !starts_with(name, "trailers") &&
+		     !starts_with(name, "contents")))

We have 4 "real" object types, commit, tree, blob, tag. Do you really mean "not tag or commit" here, don't you mean "is tree or blob" instead? I.e. do we really want to pass OBJ_NONE etc. here?

Quoted 35 lines
ref-filter.c +6 -4
 			continue;
 		if (!subpos)
 			find_subpos(buf,
@@ -1438,17 +1442,19 @@ static void fill_missing_values(struct atom_value *val)
  * pointed at by the ref itself; otherwise it is the object the
  * ref (which is a tag) refers to.
  */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
+static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
 {
+	void *buf = data->content;
+
 	switch (obj->type) {
 	case OBJ_TAG:
 		grab_tag_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("tagger", val, deref, buf);
 		break;
 	case OBJ_COMMIT:
 		grab_commit_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("author", val, deref, buf);
 		grab_person("committer", val, deref, buf);
 		break;
@@ -1678,7 +1684,7 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 			return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
 					       oid_to_hex(&oi->oid), ref->refname);
 		}
-		grab_values(ref->value, deref, *obj, oi->content);
+		grab_values(ref->value, deref, *obj, oi);
 	}
 
 	grab_common_values(ref->value, deref, oi);
#3 lore raw

Re: [PATCH 1/8] [GSOC] ref-filter: add obj-type check in grab contents

Quoted 7 lines · Ævar Arnfjörð Bjarmason
Quoted 7 lines
ref-filter.c +3 -4
-		if (strcmp(name, "body") &&
-		    !starts_with(name, "subject") &&
-		    !starts_with(name, "trailers") &&
-		    !starts_with(name, "contents"))
+
+		if ((data->type != OBJ_TAG &&
+		     data->type != OBJ_COMMIT) ||
Quoted 10 lines

ref-filter.c static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf) · line 1381

Quoted 4 lines
ref-filter.c +4 -0
+		    (strcmp(name, "body") &&
+		     !starts_with(name, "subject") &&
+		     !starts_with(name, "trailers") &&
+		     !starts_with(name, "contents")))

We have 4 "real" object types, commit, tree, blob, tag. Do you really mean "not tag or commit" here, don't you mean "is tree or blob" instead? I.e. do we really want to pass OBJ_NONE etc. here?

Quoted 1 line

continue;

If somebody throws OBJ_NONE at us by mistake, we do not want to handle such an object and try to extract the subject member from it anyway, no?

The intent of the code here, before the patch, is that "what we do after the control flow passes this point is about the body, subject, trailers, and contents request, so everybody else should go to the next iteration". The caller used to give us an object compatible with these four types of requests, now the caller may throw others, hence "by the way, we know these four kinds of requests make sense only for tags and commits, so everybody else should go to the next iteration, too" would be a natural thing to add. So in that sense, I prefer it over "we know these four types of requests do not make sense for blobs and trees".

#4 lore raw

PATCH 2/8 ref-filter: add %(raw) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add new formatting option %(raw), which will print the raw object data without any changes. It will help further to migrate all cat-file formatting logic from cat-file to ref-filter.

The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).

E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.

Therefore, add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

---
 Documentation/git-for-each-ref.txt |   9 ++
 ref-filter.c                       | 139 +++++++++++++++----
 t/t6300-for-each-ref.sh            | 207 +++++++++++++++++++++++++++++
 3 files changed, 328 insertions(+), 27 deletions(-)
Documentation/git-for-each-ref.txt +9 -0
index 2ae2478de706..7f1f0a1ca3b6 100644
@@ -235,6 +235,15 @@ and `date` to extract the named component.  For email fields (`authoremail`,
 without angle brackets, and `:localpart` to get the part before the `@` symbol
 out of the trimmed email.
 
+The raw data in an object is `raw`.
+
+raw:size::
+	The raw data size of the object.
+
+Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
+`--perl` because the host language may not support arbitrary binary data in the
+variables of its string type.
+
 The message in a commit or a tag object is `contents`, from which
 `contents:<part>` can be used to extract various parts out of:
 
ref-filter.c +112 -27

293 lines not on this page · raw message

t/t6300-for-each-ref.sh +207 -0

249 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#5 lore raw

Re: [PATCH 2/8] [GSOC] ref-filter: add %(raw) atom

Quoted 5 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add new formatting option %(raw), which will print the raw object data without any changes. It will help further to migrate all cat-file formatting logic from cat-file to ref-filter.

Nice goal and feature to have.

Quoted 4 lines

The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).

Quoted 8 lines

E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.

Therefore, add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.

Most of the functions that deal with this already use a strbuf in some way, before we had a const char *, now there's a size_t to go along with it, why not simply use a strbuf in the struct for the data? You'll then get the size and \0 handling for free, and any functions to deal with conversion can stick to the strbuf API, there seems to be a lot of back and forth now.

Quoted 4 lines

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

Perl at least deals with that just fine, and to the extent that it doesn't any new problems here would have nothing to do with \0 being in the data. Perl doesn't have a notion of "binary has \0 in it", it always supports \0, it has a notion of "is it utf-8 or not?", so any encoding problems wouldn't be new. I'd think that the same would be true of Python, but I'm not sure.

t/t6300-for-each-ref.sh test_atom tag contents 'Tagging at 1151968727 · line 231

Quoted 6 lines
t/t6300-for-each-ref.sh +6 -0
+test_expect_success 'basic atom: refs/tags/testtag *raw' '
+	git cat-file commit refs/tags/testtag^{} >expected &&
+	git for-each-ref --format="%(*raw)" refs/tags/testtag >actual &&
+	sanitize_pgp <expected >expected.clean &&
+	sanitize_pgp <actual >actual.clean &&
+	echo "" >>expected.clean &&

Just "echo" will do, ditto for the rest. Also odd to go back and forth between populating expected.clean & actual.clean.

Quoted 7 lines
t/t6300-for-each-ref.sh +7 -0
+test_expect_success 'set up refs pointing to binary blob' '
+	printf "a\0b\0c" >blob1 &&
+	printf "a\0c\0b" >blob2 &&
+	printf "\0a\0b\0c" >blob3 &&
+	printf "abc" >blob4 &&
+	printf "\0 \0 \0 " >blob5 &&
+	printf "\0 \0a\0 " >blob6 &&

t/t6300-for-each-ref.sh test_atom refs/myblobs/first contents:body "" · line 804

Quoted 10 lines
t/t6300-for-each-ref.sh +10 -0
+	printf "  " >blob7 &&
+	>blob8 &&
+	git hash-object blob1 -w | xargs git update-ref refs/myblobs/blob1 &&
+	git hash-object blob2 -w | xargs git update-ref refs/myblobs/blob2 &&
+	git hash-object blob3 -w | xargs git update-ref refs/myblobs/blob3 &&
+	git hash-object blob4 -w | xargs git update-ref refs/myblobs/blob4 &&
+	git hash-object blob5 -w | xargs git update-ref refs/myblobs/blob5 &&
+	git hash-object blob6 -w | xargs git update-ref refs/myblobs/blob6 &&
+	git hash-object blob7 -w | xargs git update-ref refs/myblobs/blob7 &&
+	git hash-object blob8 -w | xargs git update-ref refs/myblobs/blob8

Hrm, xargs just to avoid:

    git update-ref ... $(git hash-object) ?
Quoted 9 lines
t/t6300-for-each-ref.sh +9 -0
+test_expect_success '%(raw) with --python must failed' '
+	test_must_fail git for-each-ref --format="%(raw)" --python
+'
+
+test_expect_success '%(raw) with --tcl must failed' '
+	test_must_fail git for-each-ref --format="%(raw)" --tcl
+'
+
+test_expect_success '%(raw) with --perl must failed' '

t/t6300-for-each-ref.sh test_atom refs/myblobs/first contents:body "" · line 919

Quoted 10 lines
t/t6300-for-each-ref.sh +10 -0
+	test_must_fail git for-each-ref --format="%(raw)" --perl
+'
+
+test_expect_success '%(raw) with --shell must failed' '
+	test_must_fail git for-each-ref --format="%(raw)" --shell
+'
+
+test_expect_success '%(raw) with --shell and --sort=raw must failed' '
+	test_must_fail git for-each-ref --format="%(raw)" --sort=raw --shell
+'

s/must failed/must fail/, but see question above about encoding in these languages...

#6 lore raw

Re: [PATCH 2/8] [GSOC] ref-filter: add %(raw) atom

Quoted 11 lines · Ævar Arnfjörð Bjarmason
Quoted 4 lines

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

Perl at least deals with that just fine, and to the extent that it doesn't any new problems here would have nothing to do with \0 being in the data. Perl doesn't have a notion of "binary has \0 in it", it always supports \0, it has a notion of "is it utf-8 or not?", so any encoding problems wouldn't be new. I'd think that the same would be true of Python, but I'm not sure.

During an earlier iteration long time ago, as we knew Perl is capable of handling sequence of bytes including NUL, it was decided to start more strict by rejecting binary for all languages, which can later be loosened, to limit the scope of the initial implementation.

Quoted 11 lines

t/t6300-for-each-ref.sh test_atom refs/myblobs/first contents:body "" · line 804

Quoted 8 lines
t/t6300-for-each-ref.sh +8 -0
+	git hash-object blob1 -w | xargs git update-ref refs/myblobs/blob1 &&
+	git hash-object blob2 -w | xargs git update-ref refs/myblobs/blob2 &&
+	git hash-object blob3 -w | xargs git update-ref refs/myblobs/blob3 &&
+	git hash-object blob4 -w | xargs git update-ref refs/myblobs/blob4 &&
+	git hash-object blob5 -w | xargs git update-ref refs/myblobs/blob5 &&
+	git hash-object blob6 -w | xargs git update-ref refs/myblobs/blob6 &&
+	git hash-object blob7 -w | xargs git update-ref refs/myblobs/blob7 &&
+	git hash-object blob8 -w | xargs git update-ref refs/myblobs/blob8

Hrm, xargs just to avoid:

Quoted 1 line
    git update-ref ... $(git hash-object) ?

That's horrible. Thanks for noticing.

We'd want to catch segfaults from both hash-object and update-ref. One way to do so may be

	O=$(git hash-object -w blob1) &&
	git update-ref refs/myblobs/blob1 "$O"

Thanks for a review.

#7 lore raw

Re: [PATCH 2/8] [GSOC] ref-filter: add %(raw) atom

In reply to Ævar Arnfjörð Bjarmason
Quoted 14 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年6月17日周四 下午3:16写道:
Quoted 5 lines

The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).

Quoted 8 lines

E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.

Therefore, add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.

Quoted 7 lines

Most of the functions that deal with this already use a strbuf in some way, before we had a const char *, now there's a size_t to go along with it, why not simply use a strbuf in the struct for the data? You'll then get the size and \0 handling for free, and any functions to deal with conversion can stick to the strbuf API, there seems to be a lot of back and forth now.

Yes, strbuf is a suitable choice when using <str,len> pair. But if replace v->s with strbuf, the possible changes will be larger.

Quoted 5 lines
Quoted 4 lines

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

Quoted 7 lines

Perl at least deals with that just fine, and to the extent that it doesn't any new problems here would have nothing to do with \0 being in the data. Perl doesn't have a notion of "binary has \0 in it", it always supports \0, it has a notion of "is it utf-8 or not?", so any encoding problems wouldn't be new. I'd think that the same would be true of Python, but I'm not sure.

Not python safe. See [1].
Regarding the perl language, I support Junio's point of view: it can be re-supported in the future.

Quoted 11 lines

t/t6300-for-each-ref.sh test_atom tag contents 'Tagging at 1151968727 · line 231

Quoted 7 lines
t/t6300-for-each-ref.sh +6 -0
+test_expect_success 'basic atom: refs/tags/testtag *raw' '
+     git cat-file commit refs/tags/testtag^{} >expected &&
+     git for-each-ref --format="%(*raw)" refs/tags/testtag >actual &&
+     sanitize_pgp <expected >expected.clean &&
+     sanitize_pgp <actual >actual.clean &&
+     echo "" >>expected.clean &&

Just "echo" will do, ditto for the rest. Also odd to go back and forth between populating expected.clean & actual.clean.

Are you saying that sanitize_pgp is not needed?

Quoted 19 lines
Quoted 8 lines
t/t6300-for-each-ref.sh +7 -0
+test_expect_success 'set up refs pointing to binary blob' '
+     printf "a\0b\0c" >blob1 &&
+     printf "a\0c\0b" >blob2 &&
+     printf "\0a\0b\0c" >blob3 &&
+     printf "abc" >blob4 &&
+     printf "\0 \0 \0 " >blob5 &&
+     printf "\0 \0a\0 " >blob6 &&

t/t6300-for-each-ref.sh test_atom refs/myblobs/first contents:body "" · line 804

Quoted 10 lines
t/t6300-for-each-ref.sh +10 -0
+     printf "  " >blob7 &&
+     >blob8 &&
+     git hash-object blob1 -w | xargs git update-ref refs/myblobs/blob1 &&
+     git hash-object blob2 -w | xargs git update-ref refs/myblobs/blob2 &&
+     git hash-object blob3 -w | xargs git update-ref refs/myblobs/blob3 &&
+     git hash-object blob4 -w | xargs git update-ref refs/myblobs/blob4 &&
+     git hash-object blob5 -w | xargs git update-ref refs/myblobs/blob5 &&
+     git hash-object blob6 -w | xargs git update-ref refs/myblobs/blob6 &&
+     git hash-object blob7 -w | xargs git update-ref refs/myblobs/blob7 &&
+     git hash-object blob8 -w | xargs git update-ref refs/myblobs/blob8
Quoted 4 lines

Hrm, xargs just to avoid:

    git update-ref ... $(git hash-object) ?

I didn’t think about it, just for convenience.

Quoted 14 lines
Quoted 14 lines
t/t6300-for-each-ref.sh +14 -0
+test_expect_success '%(raw) with --python must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --python
+'
+
+test_expect_success '%(raw) with --tcl must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --tcl
+'
+
+test_expect_success '%(raw) with --perl must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --perl
+'
+
+test_expect_success '%(raw) with --shell must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --shell
Quoted 8 lines

t/t6300-for-each-ref.sh test_atom refs/myblobs/first contents:body "" · line 919

Quoted 5 lines

+'

t/t6300-for-each-ref.sh +4 -0
+
+test_expect_success '%(raw) with --shell and --sort=raw must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --sort=raw --shell
+'

s/must failed/must fail/, but see question above about encoding in these languages...

[1]: https://lore.kernel.org/git/CAOLTT8QR_GRm4TYk0E_eazQ+unVQODc-3L+b4V5JUN5jtZR8uA@mail.gmail.com/

Thanks for a review.
--
ZheNing Hu

#8 lore raw

Re: [PATCH 2/8] [GSOC] ref-filter: add %(raw) atom

Quoted 23 lines · ZheNing Hu
Quoted 14 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年6月17日周四 下午3:16写道:
Quoted 5 lines

The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).

Quoted 8 lines

E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.

Therefore, add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.

Quoted 7 lines

Most of the functions that deal with this already use a strbuf in some way, before we had a const char *, now there's a size_t to go along with it, why not simply use a strbuf in the struct for the data? You'll then get the size and \0 handling for free, and any functions to deal with conversion can stick to the strbuf API, there seems to be a lot of back and forth now.

Quoted 2 lines

Yes, strbuf is a suitable choice when using <str,len> pair. But if replace v->s with strbuf, the possible changes will be larger.

I for one would like to see it done that way, those changes are usually easy to read. Also it seems a large part of 2/8 is extra new code because we didn't do that, e.g. getting length differently if something is a strbuf or not, passing char*/size_t pairs to new functions etc.

Quoted 13 lines
Quoted 5 lines
Quoted 4 lines

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

Quoted 7 lines

Perl at least deals with that just fine, and to the extent that it doesn't any new problems here would have nothing to do with \0 being in the data. Perl doesn't have a notion of "binary has \0 in it", it always supports \0, it has a notion of "is it utf-8 or not?", so any encoding problems wouldn't be new. I'd think that the same would be true of Python, but I'm not sure.

Quoted 3 lines

Not python safe. See [1].
Regarding the perl language, I support Junio's point of view: it can be re-supported in the future.

Ah, I'd missed that. Anyway, if it's easy it seems you discovered that Perl deals with it correctly, so we could just have it support this.

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
t/t6300-for-each-ref.sh +6 -0
+test_expect_success 'basic atom: refs/tags/testtag *raw' '
+     git cat-file commit refs/tags/testtag^{} >expected &&
+     git for-each-ref --format="%(*raw)" refs/tags/testtag >actual &&
+     sanitize_pgp <expected >expected.clean &&
+     sanitize_pgp <actual >actual.clean &&
+     echo "" >>expected.clean &&
Quoted 5 lines
Quoted 3 lines

Just "echo" will do, ditto for the rest. Also odd to go back and forth between populating expected.clean & actual.clean.

Are you saying that sanitize_pgp is not needed?

No that instead of:

    echo "" >x

You can do:

echo >x

And also that going back and forth between populating different files is confusing, i.e. this:

echo a >x
echo c >y
echo b >>x

is better as:

echo a >x
echo b >>x
echo c >y

Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
t/t6300-for-each-ref.sh +17 -0
+test_expect_success 'set up refs pointing to binary blob' '
+     printf "a\0b\0c" >blob1 &&
+     printf "a\0c\0b" >blob2 &&
+     printf "\0a\0b\0c" >blob3 &&
+     printf "abc" >blob4 &&
+     printf "\0 \0 \0 " >blob5 &&
+     printf "\0 \0a\0 " >blob6 &&
+     printf "  " >blob7 &&
+     >blob8 &&
+     git hash-object blob1 -w | xargs git update-ref refs/myblobs/blob1 &&
+     git hash-object blob2 -w | xargs git update-ref refs/myblobs/blob2 &&
+     git hash-object blob3 -w | xargs git update-ref refs/myblobs/blob3 &&
+     git hash-object blob4 -w | xargs git update-ref refs/myblobs/blob4 &&
+     git hash-object blob5 -w | xargs git update-ref refs/myblobs/blob5 &&
+     git hash-object blob6 -w | xargs git update-ref refs/myblobs/blob6 &&
+     git hash-object blob7 -w | xargs git update-ref refs/myblobs/blob7 &&
+     git hash-object blob8 -w | xargs git update-ref refs/myblobs/blob8
Quoted 6 lines
Quoted 4 lines

Hrm, xargs just to avoid:

    git update-ref ... $(git hash-object) ?

I didn’t think about it, just for convenience.

*nod*, Junio had a good suggestion.

Quoted 27 lines
Quoted 14 lines
Quoted 14 lines
t/t6300-for-each-ref.sh +14 -0
+test_expect_success '%(raw) with --python must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --python
+'
+
+test_expect_success '%(raw) with --tcl must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --tcl
+'
+
+test_expect_success '%(raw) with --perl must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --perl
+'
+
+test_expect_success '%(raw) with --shell must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --shell
Quoted 8 lines

t/t6300-for-each-ref.sh test_atom refs/myblobs/first contents:body "" · line 919

Quoted 5 lines

+'

t/t6300-for-each-ref.sh +4 -0
+
+test_expect_success '%(raw) with --shell and --sort=raw must failed' '
+     test_must_fail git for-each-ref --format="%(raw)" --sort=raw --shell
+'

s/must failed/must fail/, but see question above about encoding in these languages...

[1]: https://lore.kernel.org/git/CAOLTT8QR_GRm4TYk0E_eazQ+unVQODc-3L+b4V5JUN5jtZR8uA@mail.gmail.com/

Thanks for a review.

#9 lore raw

Re: [PATCH 2/8] [GSOC] ref-filter: add %(raw) atom

Quoted 5 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年6月17日周四 下午10:45写道:
Quoted 1 line · ZheNing Hu
Quoted 8 lines
Quoted 2 lines

Yes, strbuf is a suitable choice when using <str,len> pair. But if replace v->s with strbuf, the possible changes will be larger.

I for one would like to see it done that way, those changes are usually easy to read. Also it seems a large part of 2/8 is extra new code because we didn't do that, e.g. getting length differently if something is a strbuf or not, passing char*/size_t pairs to new functions etc.

After some refactoring, I found that there are two problems:
1. There are a lot of codes like this in ref-filter to fill v->s:

v->s = show_ref(...)
v->s = copy_email(...)

It is very difficult to modify here: We know that show_ref() or copy_email() will allocate a block of memory to v->s, but if v->s is a strbuf, what should we do? In copy_email(), we can pass the v->s to copy_email() and use strbuf_add()/strbuf_addstr() instead of xstrdup() and xmemdupz(). But show_ref() will call external functions like shorten_unambiguous_ref(), we don’t know whether it will return us NULL or a dynamically allocated memory. If continue to pass v->s to the inner function, it is not a feasible method. Or we can use strbuf_attach() + strlen(), I'm not sure this is a good method.

2. See:

- for (i = 0; i < used_atom_cnt; i++) {

+       for (i = 0; i < used_atom_cnt; i++) {
                struct atom_value *v = &ref->value[i];

- if (v->s == NULL && used_atom[i].source == SOURCE_NONE)
+ if (v->s.len == 0 && used_atom[i].source == SOURCE_NONE)
return strbuf_addf_ret(err, -1, _("missing
object %s for %s"),

oid_to_hex(&ref->objectname), ref->refname);
        }

In the case of using strbuf, I don’t know how to distinguish between an empty strbuf and NULL. It can be easily distinguished by using c-style "const char*".

Quoted 8 lines
Quoted 4 lines

Not python safe. See [1].
Regarding the perl language, I support Junio's point of view: it can be re-supported in the future.

Ah, I'd missed that. Anyway, if it's easy it seems you discovered that Perl deals with it correctly, so we could just have it support this.

Well, it's ok, support for perl will be put in a separate commit.

Quoted 30 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
t/t6300-for-each-ref.sh +6 -0
+test_expect_success 'basic atom: refs/tags/testtag *raw' '
+     git cat-file commit refs/tags/testtag^{} >expected &&
+     git for-each-ref --format="%(*raw)" refs/tags/testtag >actual &&
+     sanitize_pgp <expected >expected.clean &&
+     sanitize_pgp <actual >actual.clean &&
+     echo "" >>expected.clean &&
Quoted 5 lines
Quoted 3 lines

Just "echo" will do, ditto for the rest. Also odd to go back and forth between populating expected.clean & actual.clean.

Are you saying that sanitize_pgp is not needed?

No that instead of:

    echo "" >x

You can do:

echo >x

And also that going back and forth between populating different files is confusing, i.e. this:

echo a >x
echo c >y
echo b >>x

Quoted 7 lines

is better as:

echo a >x
echo b >>x
echo c >y

Thanks, I get what you meant now.

Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
t/t6300-for-each-ref.sh +17 -0
+test_expect_success 'set up refs pointing to binary blob' '
+     printf "a\0b\0c" >blob1 &&
+     printf "a\0c\0b" >blob2 &&
+     printf "\0a\0b\0c" >blob3 &&
+     printf "abc" >blob4 &&
+     printf "\0 \0 \0 " >blob5 &&
+     printf "\0 \0a\0 " >blob6 &&
+     printf "  " >blob7 &&
+     >blob8 &&
+     git hash-object blob1 -w | xargs git update-ref refs/myblobs/blob1 &&
+     git hash-object blob2 -w | xargs git update-ref refs/myblobs/blob2 &&
+     git hash-object blob3 -w | xargs git update-ref refs/myblobs/blob3 &&
+     git hash-object blob4 -w | xargs git update-ref refs/myblobs/blob4 &&
+     git hash-object blob5 -w | xargs git update-ref refs/myblobs/blob5 &&
+     git hash-object blob6 -w | xargs git update-ref refs/myblobs/blob6 &&
+     git hash-object blob7 -w | xargs git update-ref refs/myblobs/blob7 &&
+     git hash-object blob8 -w | xargs git update-ref refs/myblobs/blob8
Quoted 9 lines
Quoted 6 lines
Quoted 4 lines

Hrm, xargs just to avoid:

    git update-ref ... $(git hash-object) ?

I didn’t think about it, just for convenience.

*nod*, Junio had a good suggestion.

ok.

Thanks.
--
ZheNing Hu

#10 lore raw

Re: [PATCH 2/8] [GSOC] ref-filter: add %(raw) atom

Quoted 42 lines · ZheNing Hu
Quoted 5 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年6月17日周四 下午10:45写道:
Quoted 1 line · ZheNing Hu
Quoted 8 lines
Quoted 2 lines

Yes, strbuf is a suitable choice when using <str,len> pair. But if replace v->s with strbuf, the possible changes will be larger.

I for one would like to see it done that way, those changes are usually easy to read. Also it seems a large part of 2/8 is extra new code because we didn't do that, e.g. getting length differently if something is a strbuf or not, passing char*/size_t pairs to new functions etc.

After some refactoring, I found that there are two problems:
1. There are a lot of codes like this in ref-filter to fill v->s:

v->s = show_ref(...)
v->s = copy_email(...)

It is very difficult to modify here: We know that show_ref() or copy_email() will allocate a block of memory to v->s, but if v->s is a strbuf, what should we do? In copy_email(), we can pass the v->s to copy_email() and use strbuf_add()/strbuf_addstr() instead of xstrdup() and xmemdupz(). But show_ref() will call external functions like shorten_unambiguous_ref(), we don’t know whether it will return us NULL or a dynamically allocated memory. If continue to pass v->s to the inner function, it is not a feasible method. Or we can use strbuf_attach() + strlen(), I'm not sure this is a good method.

2. See:

(quoted diff) +2 -2
-       for (i = 0; i < used_atom_cnt; i++) {
+       for (i = 0; i < used_atom_cnt; i++) {
                struct atom_value *v = &ref->value[i];
-               if (v->s == NULL && used_atom[i].source == SOURCE_NONE)
+               if (v->s.len == 0 && used_atom[i].source == SOURCE_NONE)
                        return strbuf_addf_ret(err, -1, _("missing

object %s for %s"),

Quoted 5 lines
oid_to_hex(&ref->objectname), ref->refname);
        }

In the case of using strbuf, I don’t know how to distinguish between an empty strbuf and NULL. It can be easily distinguished by using c-style "const char*".

Yes, sometimes it's just too much of a hassle, looking at shorten_unambiguous_ref() which returns a xstrdup()'d value that could indeed be strbuf_attach'd. I haven't tried the conversion myself, perhaps it's too much hassle.

Just a suggestion from reading your patch in isolation.

Quoted 83 lines
Quoted 8 lines
Quoted 4 lines

Not python safe. See [1].
Regarding the perl language, I support Junio's point of view: it can be re-supported in the future.

Ah, I'd missed that. Anyway, if it's easy it seems you discovered that Perl deals with it correctly, so we could just have it support this.

Well, it's ok, support for perl will be put in a separate commit.

Quoted 30 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
t/t6300-for-each-ref.sh +6 -0
+test_expect_success 'basic atom: refs/tags/testtag *raw' '
+     git cat-file commit refs/tags/testtag^{} >expected &&
+     git for-each-ref --format="%(*raw)" refs/tags/testtag >actual &&
+     sanitize_pgp <expected >expected.clean &&
+     sanitize_pgp <actual >actual.clean &&
+     echo "" >>expected.clean &&
Quoted 5 lines
Quoted 3 lines

Just "echo" will do, ditto for the rest. Also odd to go back and forth between populating expected.clean & actual.clean.

Are you saying that sanitize_pgp is not needed?

No that instead of:

    echo "" >x

You can do:

echo >x

And also that going back and forth between populating different files is confusing, i.e. this:

echo a >x
echo c >y
echo b >>x

Quoted 7 lines

is better as:

echo a >x
echo b >>x
echo c >y

Thanks, I get what you meant now.

Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
t/t6300-for-each-ref.sh +17 -0
+test_expect_success 'set up refs pointing to binary blob' '
+     printf "a\0b\0c" >blob1 &&
+     printf "a\0c\0b" >blob2 &&
+     printf "\0a\0b\0c" >blob3 &&
+     printf "abc" >blob4 &&
+     printf "\0 \0 \0 " >blob5 &&
+     printf "\0 \0a\0 " >blob6 &&
+     printf "  " >blob7 &&
+     >blob8 &&
+     git hash-object blob1 -w | xargs git update-ref refs/myblobs/blob1 &&
+     git hash-object blob2 -w | xargs git update-ref refs/myblobs/blob2 &&
+     git hash-object blob3 -w | xargs git update-ref refs/myblobs/blob3 &&
+     git hash-object blob4 -w | xargs git update-ref refs/myblobs/blob4 &&
+     git hash-object blob5 -w | xargs git update-ref refs/myblobs/blob5 &&
+     git hash-object blob6 -w | xargs git update-ref refs/myblobs/blob6 &&
+     git hash-object blob7 -w | xargs git update-ref refs/myblobs/blob7 &&
+     git hash-object blob8 -w | xargs git update-ref refs/myblobs/blob8
Quoted 9 lines
Quoted 6 lines
Quoted 4 lines

Hrm, xargs just to avoid:

    git update-ref ... $(git hash-object) ?

I didn’t think about it, just for convenience.

*nod*, Junio had a good suggestion.

ok.

Thanks.

#11 lore raw

Re: [PATCH 2/8] [GSOC] ref-filter: add %(raw) atom

Quoted 8 lines · Ævar Arnfjörð Bjarmason
Quoted 6 lines · ZheNing Hu

After some refactoring, I found that there are two problems:
1. There are a lot of codes like this in ref-filter to fill v->s:

v->s = show_ref(...)
v->s = copy_email(...)

Quoted 10 lines
Quoted 10 lines

It is very difficult to modify here: We know that show_ref() or copy_email() will allocate a block of memory to v->s, but if v->s is a strbuf, what should we do? In copy_email(), we can pass the v->s to copy_email() and use strbuf_add()/strbuf_addstr() instead of xstrdup() and xmemdupz(). But show_ref() will call external functions like shorten_unambiguous_ref(), we don’t know whether it will return us NULL or a dynamically allocated memory. If continue to pass v->s to the inner function, it is not a feasible method. Or we can use strbuf_attach() + strlen(), I'm not sure this is a good method.

If you resend this patch, it might be a good idea to add a short version of the above explanations into the commit message.

[...]

Quoted 2 lines
Quoted 2 lines

In the case of using strbuf, I don’t know how to distinguish between an empty strbuf and NULL. It can be easily distinguished by using c-style "const char*".

Maybe this could also be part of the explanation.

Quoted 6 lines

Yes, sometimes it's just too much of a hassle, looking at shorten_unambiguous_ref() which returns a xstrdup()'d value that could indeed be strbuf_attach'd. I haven't tried the conversion myself, perhaps it's too much hassle.

Just a suggestion from reading your patch in isolation.

Yeah, thanks for the review anyway!

#12 lore raw

PATCH 3/8 ref-filter: use non-const ref_format in *_atom_parser()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Use non-const ref_format in *_atom_parser(), which can help us modify the members of ref_format in *_atom_parser().

---
 builtin/tag.c |  2 +-
 ref-filter.c  | 44 ++++++++++++++++++++++----------------------
 ref-filter.h  |  4 ++--
 3 files changed, 25 insertions(+), 25 deletions(-)
builtin/tag.c +1 -1
index 82fcfc098242..452558ec9575 100644
@@ -146,7 +146,7 @@ static int verify_tag(const char *name, const char *ref,
 		      const struct object_id *oid, void *cb_data)
 {
 	int flags;
-	const struct ref_format *format = cb_data;
+	struct ref_format *format = cb_data;
 	flags = GPG_VERIFY_VERBOSE;
 
 	if (format->format)
ref-filter.c +22 -22

164 lines not on this page · raw message

ref-filter.h +2 -2
index baf72a718965..74fb423fc89f 100644
@@ -116,7 +116,7 @@ void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
 void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, unsigned int mask, int on);
 /*  Based on the given format and quote_style, fill the strbuf */
 int format_ref_array_item(struct ref_array_item *info,
-			  const struct ref_format *format,
+			  struct ref_format *format,
 			  struct strbuf *final_buf,
 			  struct strbuf *error_buf);
 /*  Parse a single sort specifier and add it to the list */
@@ -137,7 +137,7 @@ void setup_ref_filter_porcelain_msg(void);
  * name must be a fully qualified refname.
  */
 void pretty_print_ref(const char *name, const struct object_id *oid,
-		      const struct ref_format *format);
+		      struct ref_format *format);
 
 /*
  * Push a single ref onto the array; this can be used to construct your own
Signature · 1 line
gitgitgadget
#13 lore raw

PATCH 4/8 ref-filter: add %(rest) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let "cat-file --batch=%(rest)" use the ref-filter interface, add %(rest) atom for ref-filter. "git for-each-ref", "git branch", "git tag" and "git verify-tag" will reject %(rest) by default.

---
 ref-filter.c             | 21 +++++++++++++++++++++
 ref-filter.h             |  5 ++++-
 t/t3203-branch-output.sh |  4 ++++
 t/t6300-for-each-ref.sh  |  4 ++++
 t/t7004-tag.sh           |  4 ++++
 t/t7030-verify-tag.sh    |  4 ++++
 6 files changed, 41 insertions(+), 1 deletion(-)
ref-filter.c +21 -0
index af8c15aef44d..8868cf98f090 100644
@@ -157,6 +157,7 @@ enum atom_type {
 	ATOM_IF,
 	ATOM_THEN,
 	ATOM_ELSE,
+	ATOM_REST,
 };
 
 /*
@@ -559,6 +560,15 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
 	return 0;
 }
 
+static int rest_atom_parser(struct ref_format *format, struct used_atom *atom,
+			    const char *arg, struct strbuf *err)
+{
+	if (arg)
+		return strbuf_addf_ret(err, -1, _("%%(rest) does not take arguments"));
+	format->use_rest = 1;
+	return 0;
+}
+
 static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
 			    const char *arg, struct strbuf *unused_err)
 {
@@ -615,6 +625,7 @@ static struct {
 	[ATOM_IF] = { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
 	[ATOM_THEN] = { "then", SOURCE_NONE },
 	[ATOM_ELSE] = { "else", SOURCE_NONE },
+	[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
 	/*
 	 * Please update $__git_ref_fieldlist in git-completion.bash
 	 * when you add new atoms
@@ -1006,6 +1017,9 @@ int verify_ref_format(struct ref_format *format)
 		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
 		if (at < 0)
 			die("%s", err.buf);
+		if (used_atom[at].atom_type == ATOM_REST)
+			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+
 		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
 		    used_atom[at].u.raw_data.option == RAW_BARE)
 			die(_("--format=%.*s cannot be used with"
@@ -1920,6 +1934,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 			v->handler = else_atom_handler;
 			v->s = xstrdup("");
 			continue;
+		} else if (atom_type == ATOM_REST) {
+			if (ref->rest)
+				v->s = xstrdup(ref->rest);
+			else
+				v->s = xstrdup("");
+			continue;
 		} else
 			continue;
 
@@ -2137,6 +2157,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
 
 	FLEX_ALLOC_STR(ref, refname, refname);
 	oidcpy(&ref->objectname, oid);
+	ref->rest = NULL;
 
 	return ref;
 }
ref-filter.h +4 -1
index 74fb423fc89f..9dc07476a584 100644
@@ -38,6 +38,7 @@ struct ref_sorting {
 
 struct ref_array_item {
 	struct object_id objectname;
+	const char *rest;
 	int flag;
 	unsigned int kind;
 	const char *symref;
@@ -76,14 +77,16 @@ struct ref_format {
 	 * verify_ref_format() afterwards to finalize.
 	 */
 	const char *format;
+	const char *rest;
 	int quote_style;
+	int use_rest;
 	int use_color;
 
 	/* Internal state to ref-filter */
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
t/t3203-branch-output.sh +4 -0
index 5325b9f67a00..2780ec8803fd 100755
@@ -340,6 +340,10 @@ test_expect_success 'git branch --format option' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch with --format=%(rest) must failed' '
+	test_must_fail git branch --format="%(rest)" >actual
+'
+
 test_expect_success 'worktree colors correct' '
 	cat >expect <<-EOF &&
 	* <GREEN>(HEAD detached from fromtag)<RESET>
t/t6300-for-each-ref.sh +4 -0
index e2867de791e7..8c97c3b877c6 100755
@@ -1187,6 +1187,10 @@ test_expect_success 'basic atom: head contents:trailers' '
 	test_cmp expect actual.clean
 '
 
+test_expect_success 'basic atom: rest must failed' '
+	test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
+'
+
 test_expect_success 'trailer parsing not fooled by --- line' '
 	git commit --allow-empty -F - <<-\EOF &&
 	this is the subject
t/t7004-tag.sh +4 -0
index 2f72c5c6883e..9fc4c4323949 100755
@@ -1998,6 +1998,10 @@ test_expect_success '--format should list tags as per format given' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git tag -l with --format="%(rest)" must failed' '
+	test_must_fail git tag -l --format="%(rest)" "v1*"
+'
+
 test_expect_success "set up color tests" '
 	echo "<RED>v1.0<RESET>" >expect.color &&
 	echo "v1.0" >expect.bare &&
t/t7030-verify-tag.sh +4 -0
index 3cefde9602bf..785b32eb88f9 100755
@@ -194,6 +194,10 @@ test_expect_success GPG 'verifying tag with --format' '
 	test_cmp expect actual
 '
 
+test_expect_success GPG 'verifying tag with --format="%(rest)" must failed' '
+	test_must_fail git verify-tag --format="%(rest)" "fourth-signed"
+'
+
 test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
 	test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
 	test_must_be_empty actual-forged
Signature · 1 line
gitgitgadget
#14 lore raw

PATCH 5/8 ref-filter: teach get_object() return useful value

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Let populate_value(), get_ref_atom_value() and format_ref_array_item() get the return value of get_value() correctly. This can help us later let cat-file --batch get the correct error message and return value of get_value().

---
 ref-filter.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)
ref-filter.c +11 -8
index 8868cf98f090..420c0bf9384f 100644
@@ -1808,7 +1808,7 @@ static char *get_worktree_path(const struct used_atom *atom, const struct ref_ar
 static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
 	struct object *obj;
-	int i;
+	int i, ret = 0;
 	struct object_info empty = OBJECT_INFO_INIT;
 
 	CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -1965,8 +1965,8 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
 
 	oi.oid = ref->objectname;
-	if (get_object(ref, 0, &obj, &oi, err))
-		return -1;
+	if ((ret = get_object(ref, 0, &obj, &oi, err)))
+		return ret;
 
 	/*
 	 * If there is no atom that wants to know about tagged
@@ -1997,9 +1997,11 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 static int get_ref_atom_value(struct ref_array_item *ref, int atom,
 			      struct atom_value **v, struct strbuf *err)
 {
+	int ret = 0;
+
 	if (!ref->value) {
-		if (populate_value(ref, err))
-			return -1;
+		if ((ret = populate_value(ref, err)))
+			return ret;
 		fill_missing_values(ref->value);
 	}
 	*v = &ref->value[atom];
@@ -2573,6 +2575,7 @@ int format_ref_array_item(struct ref_array_item *info,
 {
 	const char *cp, *sp, *ep;
 	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+	int ret = 0;
 
 	state.quote_style = format->quote_style;
 	push_stack_element(&state.stack);
@@ -2585,10 +2588,10 @@ int format_ref_array_item(struct ref_array_item *info,
 		if (cp < sp)
 			append_literal(cp, sp, &state);
 		pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-		if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+		if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||
 		    atomv->handler(atomv, &state, error_buf)) {
 			pop_stack_element(&state.stack);
-			return -1;
+			return ret ? ret : -1;
 		}
 	}
 	if (*cp) {
@@ -2610,7 +2613,7 @@ int format_ref_array_item(struct ref_array_item *info,
 	}
 	strbuf_addbuf(final_buf, &state.stack->output);
 	pop_stack_element(&state.stack);
-	return 0;
+	return ret;
 }
 
 void pretty_print_ref(const char *name, const struct object_id *oid,
Signature · 1 line
gitgitgadget
#15 lore raw

Re: [PATCH 5/8] [GSOC] ref-filter: teach get_object() return useful value

Quoted 7 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Let populate_value(), get_ref_atom_value() and format_ref_array_item() get the return value of get_value() correctly. This can help us later let cat-file --batch get the correct error message and return value of get_value().

Is it get_object() or get_value()?

#16 lore raw

Re: [PATCH 5/8] [GSOC] ref-filter: teach get_object() return useful value

Quoted 4 lines · Christian Couder <[email protected]> 于2021年6月13日周日 上午4:09写道:
Quoted 1 line · ZheNing Hu via GitGitGadget
Quoted 8 lines
Quoted 6 lines

From: ZheNing Hu <[email protected]>

Let populate_value(), get_ref_atom_value() and format_ref_array_item() get the return value of get_value() correctly. This can help us later let cat-file --batch get the correct error message and return value of get_value().

Is it get_object() or get_value()?

Oh, it's get_object().

Thanks for pointing out:)
--
ZheNing Hu

#17 lore raw

Re: [PATCH 5/8] [GSOC] ref-filter: teach get_object() return useful value

In reply to ZheNing Hu via GitGitGadget
Quoted 19 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Let populate_value(), get_ref_atom_value() and format_ref_array_item() get the return value of get_value() correctly. This can help us later let cat-file --batch get the correct error message and return value of get_value().

---
 ref-filter.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)
ref-filter.c +0 -0
index 8868cf98f090..420c0bf9384f 100644

ref-filter.c static char *get_worktree_path(const struct used_atom *atom, const struct ref_ar · line 1811

Quoted 5 lines
ref-filter.c +1 -1
@@ -1808,7 +1808,7 @@ static char *get_worktree_path(const struct used_atom *atom, const struct ref_ar
 static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
 	struct object *obj;
-	int i;
+	int i, ret = 0;

The usual style is to not bunch up variables based on type, but only if they're related, i.e. we'd do:

if i, j; /* proceed to use i and j in two for-loops */

But:

int i; /* for the for-loop */
int ret = 0; /* for our return value */

(Without the comments)

ref-filter.c static int populate_value(struct ref_array_item *ref, struct strbuf *err) · line 1969

Quoted 11 lines
ref-filter.c +2 -2
 	struct object_info empty = OBJECT_INFO_INIT;
 
 	CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -1965,8 +1965,8 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
 
 	oi.oid = ref->objectname;
-	if (get_object(ref, 0, &obj, &oi, err))
-		return -1;
+	if ((ret = get_object(ref, 0, &obj, &oi, err)))
+		return ret;

Maybe more personal style, I'd just write this as:

    ret = x();
    if (!ret)
        return ret;

Makes it easier to read and balance parens in your head for the common case...

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2593

Quoted 8 lines
ref-filter.c +1 -1
@@ -2585,10 +2588,10 @@ int format_ref_array_item(struct ref_array_item *info,
 		if (cp < sp)
 			append_literal(cp, sp, &state);
 		pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-		if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+		if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||
 		    atomv->handler(atomv, &state, error_buf)) {
 			pop_stack_element(&state.stack);

... and use that mental energy on readist stuff like this, which I'd just leave as the inline assignment.

#18 lore raw

Re: [PATCH 5/8] [GSOC] ref-filter: teach get_object() return useful value

Quoted 32 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年6月17日周四 下午3:25写道:
Quoted 19 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Let populate_value(), get_ref_atom_value() and format_ref_array_item() get the return value of get_value() correctly. This can help us later let cat-file --batch get the correct error message and return value of get_value().

---
 ref-filter.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)
ref-filter.c +0 -0
index 8868cf98f090..420c0bf9384f 100644

ref-filter.c static char *get_worktree_path(const struct used_atom *atom, const struct ref_ar · line 1811

Quoted 5 lines
ref-filter.c +1 -1
@@ -1808,7 +1808,7 @@ static char *get_worktree_path(const struct used_atom *atom, const struct ref_ar
 static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
      struct object *obj;
-     int i;
+     int i, ret = 0;

The usual style is to not bunch up variables based on type, but only if they're related, i.e. we'd do:

Quoted 9 lines

if i, j; /* proceed to use i and j in two for-loops */

But:

int i; /* for the for-loop */
int ret = 0; /* for our return value */

(Without the comments)

I agree.

Quoted 14 lines

ref-filter.c static int populate_value(struct ref_array_item *ref, struct strbuf *err) · line 1969

Quoted 11 lines
ref-filter.c +2 -2
      struct object_info empty = OBJECT_INFO_INIT;
 
      CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -1965,8 +1965,8 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
 
      oi.oid = ref->objectname;
-     if (get_object(ref, 0, &obj, &oi, err))
-             return -1;
+     if ((ret = get_object(ref, 0, &obj, &oi, err)))
+             return ret;

Maybe more personal style, I'd just write this as:

Quoted 7 lines
    ret = x();
    if (!ret)
        return ret;

Makes it easier to read and balance parens in your head for the common case...

Yeah. This way it will be more readable.

Quoted 11 lines

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2593

Quoted 8 lines
ref-filter.c +1 -1
@@ -2585,10 +2588,10 @@ int format_ref_array_item(struct ref_array_item *info,
              if (cp < sp)
                      append_literal(cp, sp, &state);
              pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-             if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+             if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||
                  atomv->handler(atomv, &state, error_buf)) {
                      pop_stack_element(&state.stack);

... and use that mental energy on readist stuff like this, which I'd just leave as the inline assignment.

Indeed, inline assignment must be used in this case.

Thanks. I will pay attention to these style issues later. --
ZheNing Hu

#19 lore raw

PATCH 6/8 cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let cat-file use ref-filter logic, the following methods are used:

1. Add cat_file_mode member in struct ref_format, this can help us reject atoms in verify_ref_format() which cat-file cannot use, e.g. %(refname), %(push), %(upstream)...
2. Change the type of member format in struct batch_options to ref_format, We can add format data in it.
3. Let batch_objects() add atoms to format, and use verify_ref_format() to check atoms.
4. Use has_object_file() in batch_one_object() to check whether the input object exists.
5. 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 less than zero, use die() to print the error message and exit; else return value greater than zero, only print the error message, but not exit.
6. Let get_object() return 1 and print "<oid> missing" instead of returning -1 and printing "missing object <oid> for <refname>", this can help format_ref_array_item() just report that the object is missing without letting Git exit.

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 refname.

---
 Documentation/git-cat-file.txt |   6 +
 builtin/cat-file.c             | 250 +++++++-------------------------
 ref-filter.c                   |  15 +-
 ref-filter.h                   |   3 +-
 t/t1006-cat-file.sh            | 252 +++++++++++++++++++++++++++++++++
 t/t6301-for-each-ref-errors.sh |   2 +-
 6 files changed, 324 insertions(+), 204 deletions(-)
Documentation/git-cat-file.txt +6 -0
index 4eb0421b3fd9..ef8ab952b2fa 100644
@@ -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)`.
 
builtin/cat-file.c +52 -198

350 lines not on this page · raw message

ref-filter.c +11 -4
index 420c0bf9384f..d4c88d496698 100644
@@ -1017,8 +1017,15 @@ int verify_ref_format(struct ref_format *format)
 		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
 		if (at < 0)
 			die("%s", err.buf);
-		if (used_atom[at].atom_type == ATOM_REST)
-			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+		if ((!format->cat_file_mode && used_atom[at].atom_type == ATOM_REST) ||
+		    (format->cat_file_mode && (used_atom[at].atom_type == ATOM_FLAG ||
+					       used_atom[at].atom_type == ATOM_HEAD ||
+					       used_atom[at].atom_type == ATOM_PUSH ||
+					       used_atom[at].atom_type == ATOM_REFNAME ||
+					       used_atom[at].atom_type == ATOM_SYMREF ||
+					       used_atom[at].atom_type == ATOM_UPSTREAM ||
+					       used_atom[at].atom_type == ATOM_WORKTREEPATH)))
+			die(_("this command reject atom %%(%.*s)"), (int)(ep - sp - 2), sp + 2);
 
 		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
 		    used_atom[at].u.raw_data.option == RAW_BARE)
@@ -1735,8 +1742,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 	}
 	if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
 				     OBJECT_INFO_LOOKUP_REPLACE))
-		return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
-				       oid_to_hex(&oi->oid), ref->refname);
+		return strbuf_addf_ret(err, 1, _("%s missing"),
+				       oid_to_hex(&oi->oid));
 	if (oi->info.disk_sizep && oi->disk_size < 0)
 		BUG("Object size is less than zero.");
 
ref-filter.h +2 -1
index 9dc07476a584..bece9583cf18 100644
@@ -78,6 +78,7 @@ struct ref_format {
 	 */
 	const char *format;
 	const char *rest;
+	int cat_file_mode;
 	int quote_style;
 	int use_rest;
 	int use_color;
@@ -86,7 +87,7 @@ struct ref_format {
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
t/t1006-cat-file.sh +252 -0

256 lines not on this page · raw message

t/t6301-for-each-ref-errors.sh +1 -1
index 40edf9dab534..3553f84a00c1 100755
@@ -41,7 +41,7 @@ test_expect_success 'Missing objects are reported correctly' '
 	r=refs/heads/missing &&
 	echo $MISSING >.git/$r &&
 	test_when_finished "rm -f .git/$r" &&
-	echo "fatal: missing object $MISSING for $r" >missing-err &&
+	echo "fatal: $MISSING missing" >missing-err &&
 	test_must_fail git for-each-ref 2>err &&
 	test_cmp missing-err err &&
 	(
Signature · 1 line
gitgitgadget
#20 lore raw

PATCH 7/8 cat-file: reuse err buf in batch_objet_write()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Reuse the err buffer in batch_object_write(), as the buffer scratch does. This will reduce the overhead of multiple allocations of memory of the err buffer.

---
 builtin/cat-file.c | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)
builtin/cat-file.c +14 -10
index 0bc524e656e1..1a73c3d23dde 100644
@@ -212,33 +212,32 @@ static void batch_write(struct batch_options *opt, const void *data, int len)
 
 static void batch_object_write(const char *obj_name,
 			       struct strbuf *scratch,
+			       struct strbuf *err,
 			       struct batch_options *opt,
 			       struct expand_data *data)
 {
 	int ret = 0;
-	struct strbuf err = STRBUF_INIT;
 	struct ref_array_item item = { data->oid, data->rest };
 
 	strbuf_reset(scratch);
+	strbuf_reset(err);
 
-	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+	ret = format_ref_array_item(&item, &opt->format, scratch, err);
 	if (!ret) {
 		strbuf_addch(scratch, '\n');
 		batch_write(opt, scratch->buf, scratch->len);
-		strbuf_release(&err);
 	} else if (ret < 0) {
-		die("%s\n", err.buf);
-		strbuf_release(&err);
+		die("%s\n", err->buf);
 	} else {
 		/* when ret > 0 , don't call die and print the err to stdout*/
-		printf("%s\n", err.buf);
+		printf("%s\n", err->buf);
 		fflush(stdout);
-		strbuf_release(&err);
 	}
 }
 
 static void batch_one_object(const char *obj_name,
 			     struct strbuf *scratch,
+			     struct strbuf *err,
 			     struct batch_options *opt,
 			     struct expand_data *data)
 {
@@ -292,7 +291,7 @@ static void batch_one_object(const char *obj_name,
 		return;
 	}
 
-	batch_object_write(obj_name, scratch, opt, data);
+	batch_object_write(obj_name, scratch, err, opt, data);
 }
 
 struct object_cb_data {
@@ -300,13 +299,14 @@ struct object_cb_data {
 	struct expand_data *expand;
 	struct oidset *seen;
 	struct strbuf *scratch;
+	struct strbuf *err;
 };
 
 static int batch_object_cb(const struct object_id *oid, void *vdata)
 {
 	struct object_cb_data *data = vdata;
 	oidcpy(&data->expand->oid, oid);
-	batch_object_write(NULL, data->scratch, data->opt, data->expand);
+	batch_object_write(NULL, data->scratch, data->err, data->opt, data->expand);
 	return 0;
 }
 
@@ -362,6 +362,7 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
+	struct strbuf err = STRBUF_INIT;
 	struct strbuf format = STRBUF_INIT;
 	struct expand_data data;
 	int save_warning;
@@ -390,6 +391,7 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 		cb.opt = opt;
 		cb.expand = &data;
 		cb.scratch = &output;
+		cb.err = &err;
 
 		if (opt->unordered) {
 			struct oidset seen = OIDSET_INIT;
@@ -414,6 +416,7 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 
 		strbuf_release(&format);
 		strbuf_release(&output);
+		strbuf_release(&err);
 		return 0;
 	}
 
@@ -442,11 +445,12 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 			data.rest = p;
 		}
 
-		batch_one_object(input.buf, &output, opt, &data);
+		batch_one_object(input.buf, &output, &err, opt, &data);
 	}
 	strbuf_release(&format);
 	strbuf_release(&input);
 	strbuf_release(&output);
+	strbuf_release(&err);
 	warn_on_object_refname_ambiguity = save_warning;
 	return retval;
 }
Signature · 1 line
gitgitgadget
#21 lore raw

Re: [PATCH 7/8] [GSOC] cat-file: reuse err buf in batch_objet_write()

On Sat, Jun 12 2021, ZheNing Hu via GitGitGadget wrote:

Subject: s/objet/object/

#22 lore raw

Re: [PATCH 7/8] [GSOC] cat-file: reuse err buf in batch_objet_write()

Quoted 7 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年6月17日周四 下午3:17写道:

On Sat, Jun 12 2021, ZheNing Hu via GitGitGadget wrote:

Subject: s/objet/object/

OK.

--
ZheNing Hu

#23 lore raw

PATCH 8/8 cat-file: re-implement --textconv, --filters options

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

After cat-file reuses the ref-filter logic, we re-implement the functions of --textconv and --filters options.

Add members use_textconv and use_filters in struct ref_format, and use global variables use_filters and use_textconv in ref-filter.c, so that we can filter the content of the object in get_object(). Use actual_oi to record the real expand_data:
it may point to the original oi or the act_oi processed by textconv_object() or convert_to_working_tree(). grab_values() will grab the contents of actual_oi and grab_common_values() to grab the contents of origin oi, this ensures that %(objectsize) still uses the size of the unfiltered data.

In get_object(), we made an optimization: Firstly, get the size and type of the object instead of directly getting the object data. If using --textconv, after successfully obtaining the filtered object data, an extra oid_object_info_extended() will be skipped, which can reduce the cost of object data copy; If using --filter, the data of the object first will be getted first, and then convert_to_working_tree() will be used to get the filtered object data.

---
 builtin/cat-file.c |  5 ++++
 ref-filter.c       | 66 ++++++++++++++++++++++++++++++++++++++++++++--
 ref-filter.h       |  5 ++--
 3 files changed, 72 insertions(+), 4 deletions(-)
builtin/cat-file.c +5 -0
index 1a73c3d23dde..3fde2587201b 100644
@@ -376,6 +376,11 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 	if (opt->print_contents)
 		strbuf_addstr(&format, "\n%(raw)");
 	opt->format.format = format.buf;
+	if (opt->cmdmode == 'c')
+		opt->format.use_textconv = 1;
+	if (opt->cmdmode == 'w')
+		opt->format.use_filters = 1;
+
 	if (verify_ref_format(&opt->format))
 		usage_with_options(cat_file_usage, options);
 
ref-filter.c +64 -2

108 lines not on this page · raw message

ref-filter.h +3 -2
index bece9583cf18..cf7bad4e8b49 100644
@@ -80,14 +80,15 @@ struct ref_format {
 	const char *rest;
 	int cat_file_mode;
 	int quote_style;
+	int use_textconv;
+	int use_filters;
 	int use_rest;
 	int use_color;
-
 	/* Internal state to ref-filter */
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, 0, 0, -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
Signature · 1 line
gitgitgadget
#24 lore raw

Re: [PATCH 8/8] [GSOC] cat-file: re-implement --textconv, --filters options

builtin/cat-file.c static int batch_objects(struct batch_options *opt, const struct option *options · line 383

Quoted 8 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

builtin/cat-file.c +5 -0
 	opt->format.format = format.buf;
+	if (opt->cmdmode == 'c')
+		opt->format.use_textconv = 1;
+	if (opt->cmdmode == 'w')
+		opt->format.use_filters = 1;
+

Style nit: if/if -> if/else if, both can't be true.

ref-filter.c static int get_object(struct ref_array_item *ref, int deref, struct object **obj · line 1763

Quoted 9 lines
ref-filter.c +9 -0
+		/* get the type and size */
+		if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
+					OBJECT_INFO_LOOKUP_REPLACE))
+			return strbuf_addf_ret(err, 1, _("%s missing"),
+					       oid_to_hex(&oi->oid));
+
+		oi->info.sizep = NULL;
+		oi->info.typep = NULL;
+		oi->info.contentp = temp_contentp;

Here we have a call function and error if bad, then proceed to populate stuff (good)...

Quoted 6 lines
ref-filter.c +6 -0
+
+		if (use_textconv) {
+			act_oi = *oi;
+
+			if(!ref->rest)
+				return strbuf_addf_ret(err, -1, _("missing path for '%s'"),

ref-filter.c static int get_object(struct ref_array_item *ref, int deref, struct object **obj · line 1779

Quoted 10 lines
ref-filter.c +10 -0
+						       oid_to_hex(&act_oi.oid));
+			if (act_oi.type == OBJ_BLOB) {
+				if (textconv_object(the_repository,
+						    ref->rest, 0100644, &act_oi.oid,
+						    1, (char **)(&act_oi.content), &act_oi.size)) {
+					actual_oi = &act_oi;
+					goto success;
+				}
+			}
+		}

Maybe change the if (A) { if (B) {} ) to if (A && B) {} ?

Quoted 16 lines
ref-filter.c +8 -1
 	}
 	if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
 				     OBJECT_INFO_LOOKUP_REPLACE))
@@ -1748,19 +1786,43 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 		BUG("Object size is less than zero.");
 
 	if (oi->info.contentp) {
-		*obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
+		if (use_filters) {
+			if(!ref->rest)
+				return strbuf_addf_ret(err, -1, _("missing path for '%s'"),
+						       oid_to_hex(&oi->oid));
+			if (oi->type == OBJ_BLOB) {
+				struct strbuf strbuf = STRBUF_INIT;
+				struct checkout_metadata meta;
+				act_oi = *oi;

ref-filter.c static int get_object(struct ref_array_item *ref, int deref, struct object **obj · line 1806

Quoted 10 lines
ref-filter.c +10 -0
+
+				init_checkout_metadata(&meta, NULL, NULL, &act_oi.oid);
+				if (convert_to_working_tree(&the_index, ref->rest, act_oi.content, act_oi.size, &strbuf, &meta)) {
+					act_oi.size = strbuf.len;
+					act_oi.content = strbuf_detach(&strbuf, NULL);
+					actual_oi = &act_oi;
+				} else {
+					die("could not convert '%s' %s",
+					    oid_to_hex(&oi->oid), ref->rest);
+				}

... but here instead of "if (!x) { bad } do stuff" we have if (x) {do stuff} else { bad }". Better to get the die out of the way, and avoid the indentation on the "do stuff" IMO.

ref-filter.h struct ref_format { · line 91

Quoted 2 lines
ref-filter.h +1 -1
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, 0, 0, -1 }

Not a new problem, but an earlier cleanup to simply change this to designated initializers would be welcome, see recent work of mine in fsck.h for an example.

I.e. we keep churning on changing this *_INIT just to populate the one -1 field at the end, can also be simply:

    #define FOO_INIT { .that_field = 1 }
#25 lore raw

Re: [PATCH 8/8] [GSOC] cat-file: re-implement --textconv, --filters options

Quoted 14 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年6月17日周四 下午3:22写道:

builtin/cat-file.c static int batch_objects(struct batch_options *opt, const struct option *options · line 383

Quoted 8 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

      opt->format.format = format.buf;
builtin/cat-file.c +5 -0
+     if (opt->cmdmode == 'c')
+             opt->format.use_textconv = 1;
+     if (opt->cmdmode == 'w')
+             opt->format.use_filters = 1;
+
Quoted 2 lines

Style nit: if/if -> if/else if, both can't be true.

Ok.

Quoted 24 lines

ref-filter.c static int get_object(struct ref_array_item *ref, int deref, struct object **obj · line 1763

Quoted 9 lines
ref-filter.c +9 -0
+             /* get the type and size */
+             if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
+                                     OBJECT_INFO_LOOKUP_REPLACE))
+                     return strbuf_addf_ret(err, 1, _("%s missing"),
+                                            oid_to_hex(&oi->oid));
+
+             oi->info.sizep = NULL;
+             oi->info.typep = NULL;
+             oi->info.contentp = temp_contentp;

Here we have a call function and error if bad, then proceed to populate stuff (good)...

Quoted 11 lines
ref-filter.c +11 -0
+
+             if (use_textconv) {
+                     act_oi = *oi;
+
+                     if(!ref->rest)
+                             return strbuf_addf_ret(err, -1, _("missing path for '%s'"),
+                                                    oid_to_hex(&act_oi.oid));
+                     if (act_oi.type == OBJ_BLOB) {
+                             if (textconv_object(the_repository,
+                                                 ref->rest, 0100644, &act_oi.oid,
+                                                 1, (char **)(&act_oi.content), &act_oi.size)) {
Quoted 9 lines

ref-filter.c static int get_object(struct ref_array_item *ref, int deref, struct object **obj · line 1779

Quoted 5 lines
ref-filter.c +5 -0
+                                     actual_oi = &act_oi;
+                                     goto success;
+                             }
+                     }
+             }

Maybe change the if (A) { if (B) {} ) to if (A && B) {} ?

Maybe it's something like this:

@@ -1762,19 +1762,17 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj

                oi->info.typep = NULL;
                oi->info.contentp = temp_contentp;

- if (use_textconv) {
- act_oi = *oi;
+ if (use_textconv && !ref->rest)
+ return strbuf_addf_ret(err, -1, _("missing
path for '%s'"),
+ oid_to_hex(&act_oi.oid));

- if(!ref->rest)
- return strbuf_addf_ret(err, -1,
_("missing path for '%s'"),
- oid_to_hex(&act_oi.oid));
- if (act_oi.type == OBJ_BLOB) {
- if (textconv_object(the_repository,
- ref->rest,
0100644, &act_oi.oid,
- 1, (char
**)(&act_oi.content), &act_oi.size)) {
- actual_oi = &act_oi;
- goto success;
- }
+ if (use_textconv && oi->type == OBJ_BLOB) {
+ act_oi = *oi;
+ if (textconv_object(the_repository,
+ ref->rest, 0100644, &act_oi.oid,
+ 1, (char
**)(&act_oi.content), &act_oi.size)) {
+ actual_oi = &act_oi;

+                               goto success;
                        }
                }
        }
Quoted 22 lines
Quoted 22 lines
      }
      if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
                                   OBJECT_INFO_LOOKUP_REPLACE))
ref-filter.c +14 -1
@@ -1748,19 +1786,43 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
              BUG("Object size is less than zero.");
 
      if (oi->info.contentp) {
-             *obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
+             if (use_filters) {
+                     if(!ref->rest)
+                             return strbuf_addf_ret(err, -1, _("missing path for '%s'"),
+                                                    oid_to_hex(&oi->oid));
+                     if (oi->type == OBJ_BLOB) {
+                             struct strbuf strbuf = STRBUF_INIT;
+                             struct checkout_metadata meta;
+                             act_oi = *oi;
+
+                             init_checkout_metadata(&meta, NULL, NULL, &act_oi.oid);
+                             if (convert_to_working_tree(&the_index, ref->rest, act_oi.content, act_oi.size, &strbuf, &meta)) {
+                                     act_oi.size = strbuf.len;
+                                     act_oi.content = strbuf_detach(&strbuf, NULL);
+                                     actual_oi = &act_oi;
Quoted 9 lines

ref-filter.c static int get_object(struct ref_array_item *ref, int deref, struct object **obj · line 1806

Quoted 4 lines
ref-filter.c +4 -0
+                             } else {
+                                     die("could not convert '%s' %s",
+                                         oid_to_hex(&oi->oid), ref->rest);
+                             }

... but here instead of "if (!x) { bad } do stuff" we have if (x) {do stuff} else { bad }". Better to get the die out of the way, and avoid the indentation on the "do stuff" IMO.

And this part:

@@ -1786,25 +1784,21 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj

                BUG("Object size is less than zero.");

        if (oi->info.contentp) {

- if (use_filters) {
- if(!ref->rest)
- return strbuf_addf_ret(err, -1,
_("missing path for '%s'"),
- oid_to_hex(&oi->oid));
- if (oi->type == OBJ_BLOB) {

-                               struct strbuf strbuf = STRBUF_INIT;
-                               struct checkout_metadata meta;
-                               act_oi = *oi;

-
- init_checkout_metadata(&meta, NULL,
NULL, &act_oi.oid);
- if
(convert_to_working_tree(&the_index, ref->rest, act_oi.content, act_oi.size, &strbuf, &meta)) {
- act_oi.size = strbuf.len;
- act_oi.content =
strbuf_detach(&strbuf, NULL);
- actual_oi = &act_oi;
- } else {
- die("could not convert '%s' %s",
- oid_to_hex(&oi->oid), ref->rest);
- }
- }
+ if (use_filters && !ref->rest)
+ return strbuf_addf_ret(err, -1, _("missing
path for '%s'"),
+ oid_to_hex(&oi->oid));
+ if (use_filters && oi->type == OBJ_BLOB) {

+                       struct strbuf strbuf = STRBUF_INIT;
+                       struct checkout_metadata meta;
+                       act_oi = *oi;

+
+ init_checkout_metadata(&meta, NULL, NULL, &act_oi.oid);
+ if (!convert_to_working_tree(&the_index,
ref->rest, act_oi.content, act_oi.size, &strbuf, &meta))
+ die("could not convert '%s' %s",
+ oid_to_hex(&oi->oid), ref->rest);

+                       act_oi.size = strbuf.len;
+                       act_oi.content = strbuf_detach(&strbuf, NULL);
+                       actual_oi = &act_oi;
                }
Quoted 8 lines

ref-filter.h struct ref_format { · line 91

Quoted 3 lines
ref-filter.h +1 -1
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, 0, 0, -1 }

Not a new problem, but an earlier cleanup to simply change this to designated initializers would be welcome, see recent work of mine in fsck.h for an example.

Quoted 5 lines

I.e. we keep churning on changing this *_INIT just to populate the one -1 field at the end, can also be simply:

    #define FOO_INIT { .that_field = 1 }

I agree, this method is better. We don't need to care about which members so many "0" point to.

Thanks.
--
ZheNing Hu

#26 lore raw

v2 cover cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic, which based on 5a5b5f78 ([GSOC] ref-filter: add %(rest) atom)

Change from last version:

1. Use free_array_item_internal() to solve the memory leak problem.
2. Change commit message of ([GSOC] ref-filter: teach get_object() return useful value).

ZheNing Hu (9):
[GSOC] ref-filter: add obj-type check in grab contents [GSOC] ref-filter: add %(raw) atom
[GSOC] ref-filter: use non-const ref_format in *_atom_parser() [GSOC] ref-filter: add %(rest) atom
[GSOC] ref-filter: teach get_object() return useful value [GSOC] ref-filter: introduce free_array_item_internal() function [GSOC] cat-file: reuse ref-filter logic
[GSOC] cat-file: reuse err buf in batch_objet_write() [GSOC] cat-file: re-implement --textconv, --filters options

 Documentation/git-cat-file.txt     |   6 +
 Documentation/git-for-each-ref.txt |   9 +
 builtin/cat-file.c                 | 267 ++++++-----------------
 builtin/tag.c                      |   2 +-
 ref-filter.c                       | 331 ++++++++++++++++++++++-------
 ref-filter.h                       |  14 +-
 t/t1006-cat-file.sh                | 252 ++++++++++++++++++++++
 t/t3203-branch-output.sh           |   4 +
 t/t6300-for-each-ref.sh            | 211 ++++++++++++++++++
 t/t6301-for-each-ref-errors.sh     |   2 +-
 t/t7004-tag.sh                     |   4 +
 t/t7030-verify-tag.sh              |   4 +
 12 files changed, 829 insertions(+), 277 deletions(-)

base-commit: 1197f1a46360d3ae96bd9c15908a3a6f8e562207
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-980%2Fadlternative%2Fcat-file-batch-refactor-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-980/adlternative/cat-file-batch-refactor-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/980

Range-diff vs v1:

  1:  48d256db5c34 =  1:  48d256db5c34 [GSOC] ref-filter: add obj-type check in grab contents
  2:  abee6a03becb =  2:  abee6a03becb [GSOC] ref-filter: add %(raw) atom
  3:  c99d1d070a18 =  3:  c99d1d070a18 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()
  4:  5a5b5f78aeea =  4:  5a5b5f78aeea [GSOC] ref-filter: add %(rest) atom
  5:  c208b8a45d66 !  5:  49063372e003 [GSOC] ref-filter: teach get_object() return useful value

@@ Commit message
[GSOC] ref-filter: teach get_object() return useful value

Let populate_value(), get_ref_atom_value() and
- format_ref_array_item() get the return value of get_value()
+ format_ref_array_item() get the return value of get_object()
correctly. This can help us later let cat-file --batch get the
- correct error message and return value of get_value().
+ correct error message and return value of get_object().

  -:  ------------ >  6:  d2f2563eb76a [GSOC] ref-filter: introduce free_array_item_internal() function
  6:  44ebf75e2e93 !  7:  765337a46ab0 [GSOC] cat-file: reuse ref-filter logic
     @@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi
      +	if (!ret) {
      +		strbuf_addch(scratch, '\n');
      +		batch_write(opt, scratch->buf, scratch->len);
     -+		strbuf_release(&err);
      +	} else if (ret < 0) {
      +		die("%s\n", err.buf);
     -+		strbuf_release(&err);
      +	} else {
      +		/* when ret > 0 , don't call die and print the err to stdout*/
      +		printf("%s\n", err.buf);
      +		fflush(stdout);
     -+		strbuf_release(&err);
       	}
     ++	free_array_item_internal(&item);
     ++	strbuf_release(&err);
       }
       
     + static void batch_one_object(const char *obj_name,
      @@ builtin/cat-file.c: static void batch_one_object(const char *obj_name,
       		return;
       	}
     @@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt)
       		return 0;
       	}
      @@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt)
     - 
       		batch_one_object(input.buf, &output, opt, &data);
       	}
     --
     + 
      +	strbuf_release(&format);
       	strbuf_release(&input);
       	strbuf_release(&output);
  7:  d31059c391d0 !  8:  058b304686fd [GSOC] cat-file: reuse err buf in batch_objet_write()
     @@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi
       	if (!ret) {
       		strbuf_addch(scratch, '\n');
       		batch_write(opt, scratch->buf, scratch->len);
     --		strbuf_release(&err);
       	} else if (ret < 0) {
      -		die("%s\n", err.buf);
     --		strbuf_release(&err);
      +		die("%s\n", err->buf);
       	} else {
       		/* when ret > 0 , don't call die and print the err to stdout*/
      -		printf("%s\n", err.buf);
      +		printf("%s\n", err->buf);
       		fflush(stdout);
     --		strbuf_release(&err);
       	}
     + 	free_array_item_internal(&item);
     +-	strbuf_release(&err);
       }
       
       static void batch_one_object(const char *obj_name,
     @@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt, const st
      -		batch_one_object(input.buf, &output, opt, &data);
      +		batch_one_object(input.buf, &output, &err, opt, &data);
       	}
     + 
       	strbuf_release(&format);
       	strbuf_release(&input);
       	strbuf_release(&output);
  8:  0004d5b24a0f !  9:  cbf7d51933ea [GSOC] cat-file: re-implement --textconv, --filters options
     @@ ref-filter.h: struct ref_format {
      +	int use_filters;
       	int use_rest;
       	int use_color;
     --
     - 	/* Internal state to ref-filter */
     + 
     +@@ ref-filter.h: struct ref_format {
       	int need_color_reset_at_eol;
       };
Signature · 1 line
gitgitgadget
#27 lore raw

PATCH v2 1/9 ref-filter: add obj-type check in grab contents

From: ZheNing Hu <[email protected]>

Only tag and commit objects use grab_sub_body_contents() to grab object contents in the current codebase. We want to teach the function to also handle blobs and trees to get their raw data, without parsing a blob (whose contents looks like a commit or a tag) incorrectly as a commit or a tag.

Skip the block of code that is specific to handling commits and tags early when the given object is of a wrong type to help later addition to handle other types of objects in this function.

---
 ref-filter.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)
ref-filter.c +15 -9
index 4db0e40ff4c6..5cee6512fbaf 100644
@@ -1356,11 +1356,12 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
 }
 
 /* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
 {
 	int i;
 	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
 	size_t sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+	void *buf = data->content;
 
 	for (i = 0; i < used_atom_cnt; i++) {
 		struct used_atom *atom = &used_atom[i];
@@ -1371,10 +1372,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
 			continue;
 		if (deref)
 			name++;
-		if (strcmp(name, "body") &&
-		    !starts_with(name, "subject") &&
-		    !starts_with(name, "trailers") &&
-		    !starts_with(name, "contents"))
+
+		if ((data->type != OBJ_TAG &&
+		     data->type != OBJ_COMMIT) ||
+		    (strcmp(name, "body") &&
+		     !starts_with(name, "subject") &&
+		     !starts_with(name, "trailers") &&
+		     !starts_with(name, "contents")))
 			continue;
 		if (!subpos)
 			find_subpos(buf,
@@ -1438,17 +1442,19 @@ static void fill_missing_values(struct atom_value *val)
  * pointed at by the ref itself; otherwise it is the object the
  * ref (which is a tag) refers to.
  */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
+static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
 {
+	void *buf = data->content;
+
 	switch (obj->type) {
 	case OBJ_TAG:
 		grab_tag_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("tagger", val, deref, buf);
 		break;
 	case OBJ_COMMIT:
 		grab_commit_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("author", val, deref, buf);
 		grab_person("committer", val, deref, buf);
 		break;
@@ -1678,7 +1684,7 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 			return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
 					       oid_to_hex(&oi->oid), ref->refname);
 		}
-		grab_values(ref->value, deref, *obj, oi->content);
+		grab_values(ref->value, deref, *obj, oi);
 	}
 
 	grab_common_values(ref->value, deref, oi);
Signature · 1 line
gitgitgadget
#28 lore raw

PATCH v2 2/9 ref-filter: add %(raw) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add new formatting option %(raw), which will print the raw object data without any changes. It will help further to migrate all cat-file formatting logic from cat-file to ref-filter.

The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).

E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.

Therefore, add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

---
 Documentation/git-for-each-ref.txt |   9 ++
 ref-filter.c                       | 139 +++++++++++++++----
 t/t6300-for-each-ref.sh            | 207 +++++++++++++++++++++++++++++
 3 files changed, 328 insertions(+), 27 deletions(-)
Documentation/git-for-each-ref.txt +9 -0
index 2ae2478de706..7f1f0a1ca3b6 100644
@@ -235,6 +235,15 @@ and `date` to extract the named component.  For email fields (`authoremail`,
 without angle brackets, and `:localpart` to get the part before the `@` symbol
 out of the trimmed email.
 
+The raw data in an object is `raw`.
+
+raw:size::
+	The raw data size of the object.
+
+Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
+`--perl` because the host language may not support arbitrary binary data in the
+variables of its string type.
+
 The message in a commit or a tag object is `contents`, from which
 `contents:<part>` can be used to extract various parts out of:
 
ref-filter.c +112 -27

293 lines not on this page · raw message

t/t6300-for-each-ref.sh +207 -0

249 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#29 lore raw

PATCH v2 3/9 ref-filter: use non-const ref_format in *_atom_parser()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Use non-const ref_format in *_atom_parser(), which can help us modify the members of ref_format in *_atom_parser().

---
 builtin/tag.c |  2 +-
 ref-filter.c  | 44 ++++++++++++++++++++++----------------------
 ref-filter.h  |  4 ++--
 3 files changed, 25 insertions(+), 25 deletions(-)
builtin/tag.c +1 -1
index 82fcfc098242..452558ec9575 100644
@@ -146,7 +146,7 @@ static int verify_tag(const char *name, const char *ref,
 		      const struct object_id *oid, void *cb_data)
 {
 	int flags;
-	const struct ref_format *format = cb_data;
+	struct ref_format *format = cb_data;
 	flags = GPG_VERIFY_VERBOSE;
 
 	if (format->format)
ref-filter.c +22 -22

164 lines not on this page · raw message

ref-filter.h +2 -2
index baf72a718965..74fb423fc89f 100644
@@ -116,7 +116,7 @@ void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
 void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, unsigned int mask, int on);
 /*  Based on the given format and quote_style, fill the strbuf */
 int format_ref_array_item(struct ref_array_item *info,
-			  const struct ref_format *format,
+			  struct ref_format *format,
 			  struct strbuf *final_buf,
 			  struct strbuf *error_buf);
 /*  Parse a single sort specifier and add it to the list */
@@ -137,7 +137,7 @@ void setup_ref_filter_porcelain_msg(void);
  * name must be a fully qualified refname.
  */
 void pretty_print_ref(const char *name, const struct object_id *oid,
-		      const struct ref_format *format);
+		      struct ref_format *format);
 
 /*
  * Push a single ref onto the array; this can be used to construct your own
Signature · 1 line
gitgitgadget
#30 lore raw

PATCH v2 4/9 ref-filter: add %(rest) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let "cat-file --batch=%(rest)" use the ref-filter interface, add %(rest) atom for ref-filter. "git for-each-ref", "git branch", "git tag" and "git verify-tag" will reject %(rest) by default.

---
 ref-filter.c             | 21 +++++++++++++++++++++
 ref-filter.h             |  5 ++++-
 t/t3203-branch-output.sh |  4 ++++
 t/t6300-for-each-ref.sh  |  4 ++++
 t/t7004-tag.sh           |  4 ++++
 t/t7030-verify-tag.sh    |  4 ++++
 6 files changed, 41 insertions(+), 1 deletion(-)
ref-filter.c +21 -0
index af8c15aef44d..8868cf98f090 100644
@@ -157,6 +157,7 @@ enum atom_type {
 	ATOM_IF,
 	ATOM_THEN,
 	ATOM_ELSE,
+	ATOM_REST,
 };
 
 /*
@@ -559,6 +560,15 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
 	return 0;
 }
 
+static int rest_atom_parser(struct ref_format *format, struct used_atom *atom,
+			    const char *arg, struct strbuf *err)
+{
+	if (arg)
+		return strbuf_addf_ret(err, -1, _("%%(rest) does not take arguments"));
+	format->use_rest = 1;
+	return 0;
+}
+
 static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
 			    const char *arg, struct strbuf *unused_err)
 {
@@ -615,6 +625,7 @@ static struct {
 	[ATOM_IF] = { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
 	[ATOM_THEN] = { "then", SOURCE_NONE },
 	[ATOM_ELSE] = { "else", SOURCE_NONE },
+	[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
 	/*
 	 * Please update $__git_ref_fieldlist in git-completion.bash
 	 * when you add new atoms
@@ -1006,6 +1017,9 @@ int verify_ref_format(struct ref_format *format)
 		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
 		if (at < 0)
 			die("%s", err.buf);
+		if (used_atom[at].atom_type == ATOM_REST)
+			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+
 		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
 		    used_atom[at].u.raw_data.option == RAW_BARE)
 			die(_("--format=%.*s cannot be used with"
@@ -1920,6 +1934,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 			v->handler = else_atom_handler;
 			v->s = xstrdup("");
 			continue;
+		} else if (atom_type == ATOM_REST) {
+			if (ref->rest)
+				v->s = xstrdup(ref->rest);
+			else
+				v->s = xstrdup("");
+			continue;
 		} else
 			continue;
 
@@ -2137,6 +2157,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
 
 	FLEX_ALLOC_STR(ref, refname, refname);
 	oidcpy(&ref->objectname, oid);
+	ref->rest = NULL;
 
 	return ref;
 }
ref-filter.h +4 -1
index 74fb423fc89f..9dc07476a584 100644
@@ -38,6 +38,7 @@ struct ref_sorting {
 
 struct ref_array_item {
 	struct object_id objectname;
+	const char *rest;
 	int flag;
 	unsigned int kind;
 	const char *symref;
@@ -76,14 +77,16 @@ struct ref_format {
 	 * verify_ref_format() afterwards to finalize.
 	 */
 	const char *format;
+	const char *rest;
 	int quote_style;
+	int use_rest;
 	int use_color;
 
 	/* Internal state to ref-filter */
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
t/t3203-branch-output.sh +4 -0
index 5325b9f67a00..2780ec8803fd 100755
@@ -340,6 +340,10 @@ test_expect_success 'git branch --format option' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch with --format=%(rest) must failed' '
+	test_must_fail git branch --format="%(rest)" >actual
+'
+
 test_expect_success 'worktree colors correct' '
 	cat >expect <<-EOF &&
 	* <GREEN>(HEAD detached from fromtag)<RESET>
t/t6300-for-each-ref.sh +4 -0
index e2867de791e7..8c97c3b877c6 100755
@@ -1187,6 +1187,10 @@ test_expect_success 'basic atom: head contents:trailers' '
 	test_cmp expect actual.clean
 '
 
+test_expect_success 'basic atom: rest must failed' '
+	test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
+'
+
 test_expect_success 'trailer parsing not fooled by --- line' '
 	git commit --allow-empty -F - <<-\EOF &&
 	this is the subject
t/t7004-tag.sh +4 -0
index 2f72c5c6883e..9fc4c4323949 100755
@@ -1998,6 +1998,10 @@ test_expect_success '--format should list tags as per format given' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git tag -l with --format="%(rest)" must failed' '
+	test_must_fail git tag -l --format="%(rest)" "v1*"
+'
+
 test_expect_success "set up color tests" '
 	echo "<RED>v1.0<RESET>" >expect.color &&
 	echo "v1.0" >expect.bare &&
t/t7030-verify-tag.sh +4 -0
index 3cefde9602bf..785b32eb88f9 100755
@@ -194,6 +194,10 @@ test_expect_success GPG 'verifying tag with --format' '
 	test_cmp expect actual
 '
 
+test_expect_success GPG 'verifying tag with --format="%(rest)" must failed' '
+	test_must_fail git verify-tag --format="%(rest)" "fourth-signed"
+'
+
 test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
 	test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
 	test_must_be_empty actual-forged
Signature · 1 line
gitgitgadget
#31 lore raw

PATCH v2 5/9 ref-filter: teach get_object() return useful value

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Let populate_value(), get_ref_atom_value() and format_ref_array_item() get the return value of get_object() correctly. This can help us later let cat-file --batch get the correct error message and return value of get_object().

---
 ref-filter.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)
ref-filter.c +11 -8
index 8868cf98f090..420c0bf9384f 100644
@@ -1808,7 +1808,7 @@ static char *get_worktree_path(const struct used_atom *atom, const struct ref_ar
 static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
 	struct object *obj;
-	int i;
+	int i, ret = 0;
 	struct object_info empty = OBJECT_INFO_INIT;
 
 	CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -1965,8 +1965,8 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
 
 	oi.oid = ref->objectname;
-	if (get_object(ref, 0, &obj, &oi, err))
-		return -1;
+	if ((ret = get_object(ref, 0, &obj, &oi, err)))
+		return ret;
 
 	/*
 	 * If there is no atom that wants to know about tagged
@@ -1997,9 +1997,11 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 static int get_ref_atom_value(struct ref_array_item *ref, int atom,
 			      struct atom_value **v, struct strbuf *err)
 {
+	int ret = 0;
+
 	if (!ref->value) {
-		if (populate_value(ref, err))
-			return -1;
+		if ((ret = populate_value(ref, err)))
+			return ret;
 		fill_missing_values(ref->value);
 	}
 	*v = &ref->value[atom];
@@ -2573,6 +2575,7 @@ int format_ref_array_item(struct ref_array_item *info,
 {
 	const char *cp, *sp, *ep;
 	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+	int ret = 0;
 
 	state.quote_style = format->quote_style;
 	push_stack_element(&state.stack);
@@ -2585,10 +2588,10 @@ int format_ref_array_item(struct ref_array_item *info,
 		if (cp < sp)
 			append_literal(cp, sp, &state);
 		pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-		if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+		if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||
 		    atomv->handler(atomv, &state, error_buf)) {
 			pop_stack_element(&state.stack);
-			return -1;
+			return ret ? ret : -1;
 		}
 	}
 	if (*cp) {
@@ -2610,7 +2613,7 @@ int format_ref_array_item(struct ref_array_item *info,
 	}
 	strbuf_addbuf(final_buf, &state.stack->output);
 	pop_stack_element(&state.stack);
-	return 0;
+	return ret;
 }
 
 void pretty_print_ref(const char *name, const struct object_id *oid,
Signature · 1 line
gitgitgadget
#32 lore raw

Re: [PATCH v2 5/9] [GSOC] ref-filter: teach get_object() return useful value

Quoted 5 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Let populate_value(), get_ref_atom_value() and format_ref_array_item() get the return value of get_object() correctly.

The "get" the value correctly, I think. What you are teaching them is to pass the return value from get_object() through the callchain to their callers.

The readers will be helped if you say what kind of errors get_object() wants to tell its callers, not just "-1" is for error, which is what populate_value() assumes to be sufficient. In other words, which non-zero returns from get_object() are interesting and why?

ref-filter.c static int populate_value(struct ref_array_item *ref, struct strbuf *err) · line 2004

Quoted 11 lines
ref-filter.c +4 -2
@@ -1997,9 +1997,11 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 static int get_ref_atom_value(struct ref_array_item *ref, int atom,
 			      struct atom_value **v, struct strbuf *err)
 {
+	int ret = 0;
+
 	if (!ref->value) {
-		if (populate_value(ref, err))
-			return -1;
+		if ((ret = populate_value(ref, err)))
+			return ret;

The new variable only needs to be in this scope, and does not have to be shown to the entire function.

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2578

Quoted 5 lines
ref-filter.c +1 -0
@@ -2573,6 +2575,7 @@ int format_ref_array_item(struct ref_array_item *info,
 {
 	const char *cp, *sp, *ep;
 	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+	int ret = 0;

This is dubious...

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2591

Quoted 8 lines
ref-filter.c +1 -1
 	state.quote_style = format->quote_style;
 	push_stack_element(&state.stack);
@@ -2585,10 +2588,10 @@ int format_ref_array_item(struct ref_array_item *info,
 		if (cp < sp)
 			append_literal(cp, sp, &state);
 		pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-		if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+		if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||

Here, if "ret" gets assigned any non-zero value, the condition is satisfied, and ...

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2594

Quoted 4 lines
ref-filter.c +1 -1
 		    atomv->handler(atomv, &state, error_buf)) {
 			pop_stack_element(&state.stack);
-			return -1;
+			return ret ? ret : -1;

... the control flow will leave this function. Therefore, ...

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2616

Quoted 9 lines
ref-filter.c +1 -1
 		}
 	}
 	if (*cp) {
@@ -2610,7 +2613,7 @@ int format_ref_array_item(struct ref_array_item *info,
 	}
 	strbuf_addbuf(final_buf, &state.stack->output);
 	pop_stack_element(&state.stack);
-	return 0;
+	return ret;

... at this point, "ret" can never be anything other than zero. Am I misreading the patch?

If I am not misreading the patch, then "ret" does not have to be globally visible in this function---it can have the same scope as "pos".

Quoted 3 lines
ref-filter.c +0 -0
 }
 
 void pretty_print_ref(const char *name, const struct object_id *oid,
#33 lore raw

Re: [PATCH v2 5/9] [GSOC] ref-filter: teach get_object() return useful value

Quoted 5 lines · Junio C Hamano <[email protected]> 于2021年6月16日周三 下午3:36写道:
Quoted 2 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Quoted 8 lines
Quoted 3 lines

Let populate_value(), get_ref_atom_value() and format_ref_array_item() get the return value of get_object() correctly.

The "get" the value correctly, I think. What you are teaching them is to pass the return value from get_object() through the callchain to their callers.

Yes, this is exactly what I meant.

Quoted 6 lines

The readers will be helped if you say what kind of errors get_object() wants to tell its callers, not just "-1" is for error, which is what populate_value() assumes to be sufficient. In other words, which non-zero returns from get_object() are interesting and why?

As stated in 765337a, We can just print the error without exiting if the return value of format_ref_array_item() is greater than 0. Therefore, the current patch is to make get_object() return a value other than -1 when an error occurs.

Quoted 6 lines
Quoted 6 lines
ref-filter.c +2 -0
@@ -1997,9 +1997,11 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 static int get_ref_atom_value(struct ref_array_item *ref, int atom,
                            struct atom_value **v, struct strbuf *err)
 {
+     int ret = 0;
+
Quoted 9 lines

ref-filter.c static int populate_value(struct ref_array_item *ref, struct strbuf *err) · line 2004

Quoted 5 lines
ref-filter.c +2 -2
      if (!ref->value) {
-             if (populate_value(ref, err))
-                     return -1;
+             if ((ret = populate_value(ref, err)))
+                     return ret;

The new variable only needs to be in this scope, and does not have to be shown to the entire function.

Makes sense.

Quoted 40 lines

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2578

Quoted 5 lines
ref-filter.c +1 -0
@@ -2573,6 +2575,7 @@ int format_ref_array_item(struct ref_array_item *info,
 {
      const char *cp, *sp, *ep;
      struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+     int ret = 0;

This is dubious...

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2591

Quoted 8 lines
ref-filter.c +1 -1
      state.quote_style = format->quote_style;
      push_stack_element(&state.stack);
@@ -2585,10 +2588,10 @@ int format_ref_array_item(struct ref_array_item *info,
              if (cp < sp)
                      append_literal(cp, sp, &state);
              pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-             if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+             if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||

Here, if "ret" gets assigned any non-zero value, the condition is satisfied, and ...

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2594

Quoted 4 lines
ref-filter.c +1 -1
                  atomv->handler(atomv, &state, error_buf)) {
                      pop_stack_element(&state.stack);
-                     return -1;
+                     return ret ? ret : -1;

... the control flow will leave this function. Therefore, ...

ref-filter.c int format_ref_array_item(struct ref_array_item *info, · line 2616

Quoted 9 lines
ref-filter.c +1 -1
              }
      }
      if (*cp) {
@@ -2610,7 +2613,7 @@ int format_ref_array_item(struct ref_array_item *info,
      }
      strbuf_addbuf(final_buf, &state.stack->output);
      pop_stack_element(&state.stack);
-     return 0;
+     return ret;

... at this point, "ret" can never be anything other than zero. Am I misreading the patch?

Quoted 4 lines

If I am not misreading the patch, then "ret" does not have to be globally visible in this function---it can have the same scope as "pos".

You are right, It is correct to only return 0 here at the moment.

Thanks.
--
ZheNing Hu

#34 lore raw

PATCH v2 6/9 ref-filter: introduce free_array_item_internal() function

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Introduce free_array_item_internal() for freeing ref_array_item value. It will be called internally by free_array_item(), and it will help cat-file --batch free ref_array_item's memory later.

---
 ref-filter.c | 11 ++++++++---
 ref-filter.h |  2 ++
 2 files changed, 10 insertions(+), 3 deletions(-)
ref-filter.c +8 -3
index 420c0bf9384f..aa2ce634106b 100644
@@ -2282,16 +2282,21 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 	return 0;
 }
 
-/*  Free memory allocated for a ref_array_item */
-static void free_array_item(struct ref_array_item *item)
+void free_array_item_internal(struct ref_array_item *item)
 {
-	free((char *)item->symref);
 	if (item->value) {
 		int i;
 		for (i = 0; i < used_atom_cnt; i++)
 			free((char *)item->value[i].s);
 		free(item->value);
 	}
+}
+
+/*  Free memory allocated for a ref_array_item */
+static void free_array_item(struct ref_array_item *item)
+{
+	free((char *)item->symref);
+	free_array_item_internal(item);
 	free(item);
 }
 
ref-filter.h +2 -0
index 9dc07476a584..d4531fef5f91 100644
@@ -111,6 +111,8 @@ struct ref_format {
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
 /*  Clear all memory allocated to ref_array */
 void ref_array_clear(struct ref_array *array);
+/* Free array item's value */
+void free_array_item_internal(struct ref_array_item *item);
 /*  Used to verify if the given format is correct and to parse out the used atoms */
 int verify_ref_format(struct ref_format *format);
 /*  Sort the given ref_array as per the ref_sorting provided */
Signature · 1 line
gitgitgadget
#35 lore raw

Re: [PATCH v2 6/9] [GSOC] ref-filter: introduce free_array_item_internal() function

Quoted 5 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Introduce free_array_item_internal() for freeing ref_array_item value. It will be called internally by free_array_item(), and it will help cat-file --batch free ref_array_item's memory later.

As a file local static function, the horrible name free_array_item() was tolerable. But before exposing a name like that to the outside world, think twice if that is specific enough, and it is not. There are 47 different kinds of "array"s we use in the system, but this new helper function only works with ref_array_item and not on items in any other kinds of arrays.

ref-filter.c static int ref_filter_handler(const char *refname, const struct object_id *oid, · line 2286

Quoted 4 lines
ref-filter.c +1 -2
-/*  Free memory allocated for a ref_array_item */
-static void free_array_item(struct ref_array_item *item)
+void free_array_item_internal(struct ref_array_item *item)
 {

And "internal" is a horrible name to have as an external name. You probably can come up with a more appropriate name when you imagine yourself explaining to somebody who is relatively new to this part of the codebase what the difference between free_array_item() and this new helper is, where the difference comes from, why the symref member (and no other member) is so special, etc.

I _think_ what is special is not the .symref but is the .value field, IOW, you are trying to come up with an interface to free the value part of ref_array_item without touching other things. But it is not helpful at all to readers if you do not explain why you want to do so. Why is the .value member so special? The ability to clear only the .value member without touching other members is useful because ...?

In any case, assuming that you'd establish why the .value member is so special to deserve an externally callable function, when external callers do not have to be able to free the item as a whole (i.e. free_array_item() is still file-scope static), in the proposed log message in an updated patch, I would imagine that

free_ref_array_item_value()

would be a more suitable name than the _internal thing. When it happens, you might want to rename the static one to free_ref_array_item() to match, even if it does not have external callers.

#36 lore raw

Re: [PATCH v2 6/9] [GSOC] ref-filter: introduce free_array_item_internal() function

Quoted 9 lines · Junio C Hamano <[email protected]> 于2021年6月16日周三 下午3:49写道:
Quoted 5 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Introduce free_array_item_internal() for freeing ref_array_item value. It will be called internally by free_array_item(), and it will help cat-file --batch free ref_array_item's memory later.

Quoted 7 lines

As a file local static function, the horrible name free_array_item() was tolerable. But before exposing a name like that to the outside world, think twice if that is specific enough, and it is not. There are 47 different kinds of "array"s we use in the system, but this new helper function only works with ref_array_item and not on items in any other kinds of arrays.

Well, this is indeed ignored by me.

So free_array_item() --> free_ref_array_item() and free_array_item() --> free_ref_array_item().

Quoted 12 lines

ref-filter.c static int ref_filter_handler(const char *refname, const struct object_id *oid, · line 2286

Quoted 4 lines
ref-filter.c +1 -2
-/*  Free memory allocated for a ref_array_item */
-static void free_array_item(struct ref_array_item *item)
+void free_array_item_internal(struct ref_array_item *item)
 {

And "internal" is a horrible name to have as an external name. You probably can come up with a more appropriate name when you imagine yourself explaining to somebody who is relatively new to this part of the codebase what the difference between free_array_item() and this new helper is, where the difference comes from, why the symref member (and no other member) is so special, etc.

Yeah, "internal" is an incorrect naming. The main purpose here is to only free a ref_array_item's value.

Quoted 8 lines

I _think_ what is special is not the .symref but is the .value field, IOW, you are trying to come up with an interface to free the value part of ref_array_item without touching other things. But it is not helpful at all to readers if you do not explain why you want to do so. Why is the .value member so special? The ability to clear only the .value member without touching other members is useful because ...?

Because batch_object_write() use a ref_array_item which is allocated on the stack, and it's member symref is not used at all. ref_array_item's symref and itself do not need free. The original free_array_item() will free all dynamically
allocated content in ref_array_item. It cannot meet our requirements at this time:
only free the ref_array_item's value.

Quoted 6 lines

In any case, assuming that you'd establish why the .value member is so special to deserve an externally callable function, when external callers do not have to be able to free the item as a whole (i.e. free_array_item() is still file-scope static), in the proposed log message in an updated patch, I would imagine that

Quoted 6 lines

free_ref_array_item_value()

would be a more suitable name than the _internal thing. When it happens, you might want to rename the static one to free_ref_array_item() to match, even if it does not have external callers.

Make sence.

--
ZheNing Hu

#37 lore raw

PATCH v2 7/9 cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let cat-file use ref-filter logic, the following methods are used:

1. Add cat_file_mode member in struct ref_format, this can help us reject atoms in verify_ref_format() which cat-file cannot use, e.g. %(refname), %(push), %(upstream)...
2. Change the type of member format in struct batch_options to ref_format, We can add format data in it.
3. Let batch_objects() add atoms to format, and use verify_ref_format() to check atoms.
4. Use has_object_file() in batch_one_object() to check whether the input object exists.
5. 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 less than zero, use die() to print the error message and exit; else return value greater than zero, only print the error message, but not exit.
6. Let get_object() return 1 and print "<oid> missing" instead of returning -1 and printing "missing object <oid> for <refname>", this can help format_ref_array_item() just report that the object is missing without letting Git exit.

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 refname.

---
 Documentation/git-cat-file.txt |   6 +
 builtin/cat-file.c             | 248 +++++++-------------------------
 ref-filter.c                   |  15 +-
 ref-filter.h                   |   3 +-
 t/t1006-cat-file.sh            | 252 +++++++++++++++++++++++++++++++++
 t/t6301-for-each-ref-errors.sh |   2 +-
 6 files changed, 323 insertions(+), 203 deletions(-)
Documentation/git-cat-file.txt +6 -0
index 4eb0421b3fd9..ef8ab952b2fa 100644
@@ -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)`.
 
builtin/cat-file.c +51 -197

349 lines not on this page · raw message

ref-filter.c +11 -4
index aa2ce634106b..660e52f97f79 100644
@@ -1017,8 +1017,15 @@ int verify_ref_format(struct ref_format *format)
 		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
 		if (at < 0)
 			die("%s", err.buf);
-		if (used_atom[at].atom_type == ATOM_REST)
-			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+		if ((!format->cat_file_mode && used_atom[at].atom_type == ATOM_REST) ||
+		    (format->cat_file_mode && (used_atom[at].atom_type == ATOM_FLAG ||
+					       used_atom[at].atom_type == ATOM_HEAD ||
+					       used_atom[at].atom_type == ATOM_PUSH ||
+					       used_atom[at].atom_type == ATOM_REFNAME ||
+					       used_atom[at].atom_type == ATOM_SYMREF ||
+					       used_atom[at].atom_type == ATOM_UPSTREAM ||
+					       used_atom[at].atom_type == ATOM_WORKTREEPATH)))
+			die(_("this command reject atom %%(%.*s)"), (int)(ep - sp - 2), sp + 2);
 
 		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
 		    used_atom[at].u.raw_data.option == RAW_BARE)
@@ -1735,8 +1742,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 	}
 	if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
 				     OBJECT_INFO_LOOKUP_REPLACE))
-		return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
-				       oid_to_hex(&oi->oid), ref->refname);
+		return strbuf_addf_ret(err, 1, _("%s missing"),
+				       oid_to_hex(&oi->oid));
 	if (oi->info.disk_sizep && oi->disk_size < 0)
 		BUG("Object size is less than zero.");
 
ref-filter.h +2 -1
index d4531fef5f91..3db07729f46d 100644
@@ -78,6 +78,7 @@ struct ref_format {
 	 */
 	const char *format;
 	const char *rest;
+	int cat_file_mode;
 	int quote_style;
 	int use_rest;
 	int use_color;
@@ -86,7 +87,7 @@ struct ref_format {
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
t/t1006-cat-file.sh +252 -0

256 lines not on this page · raw message

t/t6301-for-each-ref-errors.sh +1 -1
index 40edf9dab534..3553f84a00c1 100755
@@ -41,7 +41,7 @@ test_expect_success 'Missing objects are reported correctly' '
 	r=refs/heads/missing &&
 	echo $MISSING >.git/$r &&
 	test_when_finished "rm -f .git/$r" &&
-	echo "fatal: missing object $MISSING for $r" >missing-err &&
+	echo "fatal: $MISSING missing" >missing-err &&
 	test_must_fail git for-each-ref 2>err &&
 	test_cmp missing-err err &&
 	(
Signature · 1 line
gitgitgadget
#38 lore raw

PATCH v2 8/9 cat-file: reuse err buf in batch_objet_write()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Reuse the err buffer in batch_object_write(), as the buffer scratch does. This will reduce the overhead of multiple allocations of memory of the err buffer.

---
 builtin/cat-file.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
builtin/cat-file.c +14 -8
index 026d9405636a..6766efb06f41 100644
@@ -212,32 +212,33 @@ static void batch_write(struct batch_options *opt, const void *data, int len)
 
 static void batch_object_write(const char *obj_name,
 			       struct strbuf *scratch,
+			       struct strbuf *err,
 			       struct batch_options *opt,
 			       struct expand_data *data)
 {
 	int ret = 0;
-	struct strbuf err = STRBUF_INIT;
 	struct ref_array_item item = { data->oid, data->rest };
 
 	strbuf_reset(scratch);
+	strbuf_reset(err);
 
-	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+	ret = format_ref_array_item(&item, &opt->format, scratch, err);
 	if (!ret) {
 		strbuf_addch(scratch, '\n');
 		batch_write(opt, scratch->buf, scratch->len);
 	} else if (ret < 0) {
-		die("%s\n", err.buf);
+		die("%s\n", err->buf);
 	} else {
 		/* when ret > 0 , don't call die and print the err to stdout*/
-		printf("%s\n", err.buf);
+		printf("%s\n", err->buf);
 		fflush(stdout);
 	}
 	free_array_item_internal(&item);
-	strbuf_release(&err);
 }
 
 static void batch_one_object(const char *obj_name,
 			     struct strbuf *scratch,
+			     struct strbuf *err,
 			     struct batch_options *opt,
 			     struct expand_data *data)
 {
@@ -291,7 +292,7 @@ static void batch_one_object(const char *obj_name,
 		return;
 	}
 
-	batch_object_write(obj_name, scratch, opt, data);
+	batch_object_write(obj_name, scratch, err, opt, data);
 }
 
 struct object_cb_data {
@@ -299,13 +300,14 @@ struct object_cb_data {
 	struct expand_data *expand;
 	struct oidset *seen;
 	struct strbuf *scratch;
+	struct strbuf *err;
 };
 
 static int batch_object_cb(const struct object_id *oid, void *vdata)
 {
 	struct object_cb_data *data = vdata;
 	oidcpy(&data->expand->oid, oid);
-	batch_object_write(NULL, data->scratch, data->opt, data->expand);
+	batch_object_write(NULL, data->scratch, data->err, data->opt, data->expand);
 	return 0;
 }
 
@@ -361,6 +363,7 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
+	struct strbuf err = STRBUF_INIT;
 	struct strbuf format = STRBUF_INIT;
 	struct expand_data data;
 	int save_warning;
@@ -389,6 +392,7 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 		cb.opt = opt;
 		cb.expand = &data;
 		cb.scratch = &output;
+		cb.err = &err;
 
 		if (opt->unordered) {
 			struct oidset seen = OIDSET_INIT;
@@ -413,6 +417,7 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 
 		strbuf_release(&format);
 		strbuf_release(&output);
+		strbuf_release(&err);
 		return 0;
 	}
 
@@ -441,12 +446,13 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 			data.rest = p;
 		}
 
-		batch_one_object(input.buf, &output, opt, &data);
+		batch_one_object(input.buf, &output, &err, opt, &data);
 	}
 
 	strbuf_release(&format);
 	strbuf_release(&input);
 	strbuf_release(&output);
+	strbuf_release(&err);
 	warn_on_object_refname_ambiguity = save_warning;
 	return retval;
 }
Signature · 1 line
gitgitgadget
#39 lore raw

PATCH v2 9/9 cat-file: re-implement --textconv, --filters options

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

After cat-file reuses the ref-filter logic, we re-implement the functions of --textconv and --filters options.

Add members use_textconv and use_filters in struct ref_format, and use global variables use_filters and use_textconv in ref-filter.c, so that we can filter the content of the object in get_object(). Use actual_oi to record the real expand_data:
it may point to the original oi or the act_oi processed by textconv_object() or convert_to_working_tree(). grab_values() will grab the contents of actual_oi and grab_common_values() to grab the contents of origin oi, this ensures that %(objectsize) still uses the size of the unfiltered data.

In get_object(), we made an optimization: Firstly, get the size and type of the object instead of directly getting the object data. If using --textconv, after successfully obtaining the filtered object data, an extra oid_object_info_extended() will be skipped, which can reduce the cost of object data copy; If using --filter, the data of the object first will be getted first, and then convert_to_working_tree() will be used to get the filtered object data.

---
 builtin/cat-file.c |  5 ++++
 ref-filter.c       | 66 ++++++++++++++++++++++++++++++++++++++++++++--
 ref-filter.h       |  4 ++-
 3 files changed, 72 insertions(+), 3 deletions(-)
builtin/cat-file.c +5 -0
index 6766efb06f41..2978b8990e8d 100644
@@ -377,6 +377,11 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 	if (opt->print_contents)
 		strbuf_addstr(&format, "\n%(raw)");
 	opt->format.format = format.buf;
+	if (opt->cmdmode == 'c')
+		opt->format.use_textconv = 1;
+	if (opt->cmdmode == 'w')
+		opt->format.use_filters = 1;
+
 	if (verify_ref_format(&opt->format))
 		usage_with_options(cat_file_usage, options);
 
ref-filter.c +64 -2

108 lines not on this page · raw message

ref-filter.h +3 -1
index 3db07729f46d..56a5ec4c9eaa 100644
@@ -80,6 +80,8 @@ struct ref_format {
 	const char *rest;
 	int cat_file_mode;
 	int quote_style;
+	int use_textconv;
+	int use_filters;
 	int use_rest;
 	int use_color;
 
@@ -87,7 +89,7 @@ struct ref_format {
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, 0, 0, -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
Signature · 1 line
gitgitgadget
#40 lore raw
In reply to ZheNing Hu via GitGitGadget
Quoted 2 lines · ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic, which based on 5a5b5f78 ([GSOC] ref-filter: add %(rest) atom)

Hmph, does anybody have 5a5b5f78?

The way to deal with this and avoid resending the same patches (assuming that this is not a 9-patch series, but only 5 of them are new) is to rebase your topic on 723bc66d (ref-filter: add %(rest) atom, 2021-06-09), which will allow you to discard the 4 earlier patches, and force push, with base set to 723bc66d, I think, but I am not a GGG user, so there may need an extra step or two on top of that.

#41 lore raw
Quoted 8 lines · Junio C Hamano <[email protected]> 于2021年6月16日周三 下午3:29写道:
Quoted 2 lines · ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic, which based on 5a5b5f78 ([GSOC] ref-filter: add %(rest) atom)

Hmph, does anybody have 5a5b5f78?

Quoted 7 lines

The way to deal with this and avoid resending the same patches (assuming that this is not a 9-patch series, but only 5 of them are new) is to rebase your topic on 723bc66d (ref-filter: add %(rest) atom, 2021-06-09), which will allow you to discard the 4 earlier patches, and force push, with base set to 723bc66d, I think, but I am not a GGG user, so there may need an extra step or two on top of that.

Oh, 5a5b5f78 is in gitgitgadget.git and 723bc66d in git.git, their commit messages are different. Later I will rebase my new patch to zh/xxx.

Thanks.
--
ZheNing Hu

#42 lore raw
In reply to ZheNing Hu via GitGitGadget
Quoted 8 lines · ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic, which based on 5a5b5f78 ([GSOC] ref-filter: add %(rest) atom)

Change from last version:

1. Use free_array_item_internal() to solve the memory leak problem.
2. Change commit message of ([GSOC] ref-filter: teach get_object() return useful value).

I left some comments, but saw after the fact that I'd replied to the v1 E-Mails by accident, but anyway, the comments were all on things that are also in v2, so it worked out in the end. Sorry about the confusion.

#43 lore raw
Quoted 9 lines · Ævar Arnfjörð Bjarmason <[email protected]> 于2021年6月17日周四 下午3:27写道:
Quoted 5 lines · ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic, which based on 5a5b5f78 ([GSOC] ref-filter: add %(rest) atom)

Change from last version:

Quoted 7 lines
Quoted 3 lines

1. Use free_array_item_internal() to solve the memory leak problem.
2. Change commit message of ([GSOC] ref-filter: teach get_object() return useful value).

I left some comments, but saw after the fact that I'd replied to the v1 E-Mails by accident, but anyway, the comments were all on things that are also in v2, so it worked out in the end. Sorry about the confusion.

It's okay. Your comments are very useful.

Thanks.
--
ZheNing Hu

#44 lore raw

v3 cover cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic. As part of the content related to zh/ref-filter-raw-data has been modified, this patch currently sets the base on zh/ref-filter-atom-type until zh/ref-filter-raw-data is stable And will rebase to it.

f72ad9c~1..39a0d93 of this patch belongs to zh/ref-filter-raw-data. Change from last version:

1. Let --format=%(raw) re-support --perl.
2. Correct some errors in the test.
3. Add the reason why strbuf is not used to replace atom_value's member s in the commit message.

39a0d93..86ac3bc of this patch is another part. Change from last version:

1. Modify free_array_item_internal() to free_ref_array_item_value().
2. Change some errors of the code style.
3. Change some commit messages.

ZheNing Hu (10):
[GSOC] ref-filter: add obj-type check in grab contents [GSOC] ref-filter: add %(raw) atom
[GSOC] ref-filter: --format=%(raw) re-support --perl [GSOC] ref-filter: use non-const ref_format in *_atom_parser() [GSOC] ref-filter: add %(rest) atom
[GSOC] ref-filter: pass get_object() return value to their callers [GSOC] ref-filter: introduce free_ref_array_item_value() function [GSOC] cat-file: reuse ref-filter logic
[GSOC] cat-file: reuse err buf in batch_object_write() [GSOC] cat-file: re-implement --textconv, --filters options

 Documentation/git-cat-file.txt     |   6 +
 Documentation/git-for-each-ref.txt |   9 +
 builtin/cat-file.c                 | 267 ++++++-----------------
 builtin/tag.c                      |   2 +-
 quote.c                            |  17 ++
 quote.h                            |   1 +
 ref-filter.c                       | 331 +++++++++++++++++++++++------
 ref-filter.h                       |  14 +-
 t/t1006-cat-file.sh                | 252 ++++++++++++++++++++++
 t/t3203-branch-output.sh           |   4 +
 t/t6300-for-each-ref.sh            | 235 ++++++++++++++++++++
 t/t6301-for-each-ref-errors.sh     |   2 +-
 t/t7004-tag.sh                     |   4 +
 t/t7030-verify-tag.sh              |   4 +
 14 files changed, 872 insertions(+), 276 deletions(-)

base-commit: 1197f1a46360d3ae96bd9c15908a3a6f8e562207
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-980%2Fadlternative%2Fcat-file-batch-refactor-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-980/adlternative/cat-file-batch-refactor-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/980

Range-diff vs v2:

  1:  48d256db5c34 =  1:  f72ad9cc5e8b [GSOC] ref-filter: add obj-type check in grab contents
  2:  abee6a03becb !  2:  ab497d66c116 [GSOC] ref-filter: add %(raw) atom
     @@ Commit message
          `100644 one\0...`, only the `100644 one` will be added to the buffer,
          which is incorrect.
      
     -    Therefore, add a new member in `struct atom_value`: `s_size`, which
     -    can record raw object size, it can help us add raw object data to
     -    the buffer or compare two buffers which contain raw object data.
     +    Therefore, we need to find a way to record the length of the
     +    atom_value's member `s`. Although strbuf can already record the
     +    string and its length, if we want to replace the type of atom_value's
     +    member `s` with strbuf, many places in ref-filter that are filled
     +    with dynamically allocated mermory in `v->s` are not easy to replace.
     +    At the same time, we need to check if `v->s == NULL` in
     +    populate_value(), and strbuf cannot easily distinguish NULL and empty
     +    strings, but c-style "const char *" can do it. So add a new member in
     +    `struct atom_value`: `s_size`, which can record raw object size, it
     +    can help us add raw object data to the buffer or compare two buffers
     +    which contain raw object data.
      
          Beyond, `--format=%(raw)` cannot be used with `--python`, `--shell`,
     -    `--tcl`, `--perl` because if our binary raw data is passed to a variable
     -    in the host language, the host language may not support arbitrary binary
     -    data in the variables of its string type.
     +    `--tcl`, `--perl` because if our binary raw data is passed to a
     +    variable in the host language, the host language may not support
     +    arbitrary binary data in the variables of its string type.
      
          Mentored-by: Christian Couder <[email protected]>
          Mentored-by: Hariom Verma <[email protected]>
     +    Helped-by: Ævar Arnfjörð Bjarmason <[email protected]>
          Helped-by: Felipe Contreras <[email protected]>
          Helped-by: Phillip Wood <[email protected]>
          Helped-by: Junio C Hamano <[email protected]>
     @@ t/t6300-for-each-ref.sh: test_atom tag contents 'Tagging at 1151968727
      +	git cat-file commit refs/tags/testtag^{} >expected &&
      +	git for-each-ref --format="%(*raw)" refs/tags/testtag >actual &&
      +	sanitize_pgp <expected >expected.clean &&
     ++	echo >>expected.clean &&
      +	sanitize_pgp <actual >actual.clean &&
     -+	echo "" >>expected.clean &&
      +	test_cmp expected.clean actual.clean
      +'
      +
     @@ t/t6300-for-each-ref.sh: test_atom refs/tags/signed-empty contents:body ''
      +	git cat-file tag refs/tags/signed-empty >expected &&
      +	git for-each-ref --format="%(raw)" refs/tags/signed-empty >actual &&
      +	sanitize_pgp <expected >expected.clean &&
     ++	echo >>expected.clean &&
      +	sanitize_pgp <actual >actual.clean &&
     -+	echo "" >>expected.clean &&
      +	test_cmp expected.clean actual.clean
      +'
      +
     @@ t/t6300-for-each-ref.sh: test_atom refs/tags/signed-short contents:signature "$s
      +	git cat-file tag refs/tags/signed-short >expected &&
      +	git for-each-ref --format="%(raw)" refs/tags/signed-short >actual &&
      +	sanitize_pgp <expected >expected.clean &&
     ++	echo >>expected.clean &&
      +	sanitize_pgp <actual >actual.clean &&
     -+	echo "" >>expected.clean &&
      +	test_cmp expected.clean actual.clean
      +'
      +
     @@ t/t6300-for-each-ref.sh: test_atom refs/tags/signed-long contents "subject line
      +	git cat-file tag refs/tags/signed-long >expected &&
      +	git for-each-ref --format="%(raw)" refs/tags/signed-long >actual &&
      +	sanitize_pgp <expected >expected.clean &&
     ++	echo >>expected.clean &&
      +	sanitize_pgp <actual >actual.clean &&
     -+	echo "" >>expected.clean &&
      +	test_cmp expected.clean actual.clean
      +'
      +
     @@ t/t6300-for-each-ref.sh: test_atom refs/mytrees/first contents:body ""
       
      +test_expect_success 'basic atom: refs/mytrees/first raw' '
      +	git cat-file tree refs/mytrees/first >expected &&
     -+	echo "" >>expected &&
     ++	echo >>expected &&
      +	git for-each-ref --format="%(raw)" refs/mytrees/first >actual &&
      +	test_cmp expected actual &&
      +	git cat-file -s refs/mytrees/first >expected &&
     @@ t/t6300-for-each-ref.sh: test_atom refs/myblobs/first contents:body ""
       
      +test_expect_success 'basic atom: refs/myblobs/first raw' '
      +	git cat-file blob refs/myblobs/first >expected &&
     -+	echo "" >>expected &&
     ++	echo >>expected &&
      +	git for-each-ref --format="%(raw)" refs/myblobs/first >actual &&
      +	test_cmp expected actual &&
      +	git cat-file -s refs/myblobs/first >expected &&
     @@ t/t6300-for-each-ref.sh: test_atom refs/myblobs/first contents:body ""
      +	printf "\0 \0a\0 " >blob6 &&
      +	printf "  " >blob7 &&
      +	>blob8 &&
     -+	git hash-object blob1 -w | xargs git update-ref refs/myblobs/blob1 &&
     -+	git hash-object blob2 -w | xargs git update-ref refs/myblobs/blob2 &&
     -+	git hash-object blob3 -w | xargs git update-ref refs/myblobs/blob3 &&
     -+	git hash-object blob4 -w | xargs git update-ref refs/myblobs/blob4 &&
     -+	git hash-object blob5 -w | xargs git update-ref refs/myblobs/blob5 &&
     -+	git hash-object blob6 -w | xargs git update-ref refs/myblobs/blob6 &&
     -+	git hash-object blob7 -w | xargs git update-ref refs/myblobs/blob7 &&
     -+	git hash-object blob8 -w | xargs git update-ref refs/myblobs/blob8
     ++	obj=$(git hash-object -w blob1) &&
     ++        git update-ref refs/myblobs/blob1 "$obj" &&
     ++	obj=$(git hash-object -w blob2) &&
     ++        git update-ref refs/myblobs/blob2 "$obj" &&
     ++	obj=$(git hash-object -w blob3) &&
     ++        git update-ref refs/myblobs/blob3 "$obj" &&
     ++	obj=$(git hash-object -w blob4) &&
     ++        git update-ref refs/myblobs/blob4 "$obj" &&
     ++	obj=$(git hash-object -w blob5) &&
     ++        git update-ref refs/myblobs/blob5 "$obj" &&
     ++	obj=$(git hash-object -w blob6) &&
     ++        git update-ref refs/myblobs/blob6 "$obj" &&
     ++	obj=$(git hash-object -w blob7) &&
     ++        git update-ref refs/myblobs/blob7 "$obj" &&
     ++	obj=$(git hash-object -w blob8) &&
     ++        git update-ref refs/myblobs/blob8 "$obj"
      +'
      +
      +test_expect_success 'Verify sorts with raw' '
     @@ t/t6300-for-each-ref.sh: test_atom refs/myblobs/first contents:body ""
      +		refs/myblobs/ refs/heads/ >actual &&
      +	test_cmp expected actual
      +'
     ++
      +test_expect_success 'validate raw atom with %(if:notequals)' '
      +	cat >expected <<-EOF &&
      +	refs/heads/ambiguous
     @@ t/t6300-for-each-ref.sh: test_atom refs/myblobs/first contents:body ""
      +	test_cmp expected actual
      +'
      +
     -+test_expect_success '%(raw) with --python must failed' '
     ++test_expect_success '%(raw) with --python must fail' '
      +	test_must_fail git for-each-ref --format="%(raw)" --python
      +'
      +
     -+test_expect_success '%(raw) with --tcl must failed' '
     ++test_expect_success '%(raw) with --tcl must fail' '
      +	test_must_fail git for-each-ref --format="%(raw)" --tcl
      +'
      +
     -+test_expect_success '%(raw) with --perl must failed' '
     ++test_expect_success '%(raw) with --perl must fail' '
      +	test_must_fail git for-each-ref --format="%(raw)" --perl
      +'
      +
     -+test_expect_success '%(raw) with --shell must failed' '
     ++test_expect_success '%(raw) with --shell must fail' '
      +	test_must_fail git for-each-ref --format="%(raw)" --shell
      +'
      +
     -+test_expect_success '%(raw) with --shell and --sort=raw must failed' '
     ++test_expect_success '%(raw) with --shell and --sort=raw must fail' '
      +	test_must_fail git for-each-ref --format="%(raw)" --sort=raw --shell
      +'
      +
  -:  ------------ >  3:  b54dbc431e04 [GSOC] ref-filter: --format=%(raw) re-support --perl
  3:  c99d1d070a18 =  4:  9fbbb3c492f5 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()
  4:  5a5b5f78aeea !  5:  39a0d93c7bc1 [GSOC] ref-filter: add %(rest) atom
     @@ ref-filter.c: static struct {
       	 * Please update $__git_ref_fieldlist in git-completion.bash
       	 * when you add new atoms
      @@ ref-filter.c: int verify_ref_format(struct ref_format *format)
     - 		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
       		if (at < 0)
       			die("%s", err.buf);
     + 
      +		if (used_atom[at].atom_type == ATOM_REST)
      +			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
      +
     - 		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
     - 		    used_atom[at].u.raw_data.option == RAW_BARE)
     - 			die(_("--format=%.*s cannot be used with"
     + 		if ((format->quote_style == QUOTE_PYTHON ||
     + 		     format->quote_style == QUOTE_SHELL ||
     + 		     format->quote_style == QUOTE_TCL) &&
      @@ ref-filter.c: static int populate_value(struct ref_array_item *ref, struct strbuf *err)
       			v->handler = else_atom_handler;
       			v->s = xstrdup("");
     @@ t/t3203-branch-output.sh: test_expect_success 'git branch --format option' '
       	test_cmp expect actual
       '
       
     -+test_expect_success 'git branch with --format=%(rest) must failed' '
     ++test_expect_success 'git branch with --format=%(rest) must fail' '
      +	test_must_fail git branch --format="%(rest)" >actual
      +'
      +
     @@ t/t6300-for-each-ref.sh: test_expect_success 'basic atom: head contents:trailers
       	test_cmp expect actual.clean
       '
       
     -+test_expect_success 'basic atom: rest must failed' '
     ++test_expect_success 'basic atom: rest must fail' '
      +	test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
      +'
      +
     @@ t/t7004-tag.sh: test_expect_success '--format should list tags as per format giv
       	test_cmp expect actual
       '
       
     -+test_expect_success 'git tag -l with --format="%(rest)" must failed' '
     ++test_expect_success 'git tag -l with --format="%(rest)" must fail' '
      +	test_must_fail git tag -l --format="%(rest)" "v1*"
      +'
      +
     @@ t/t7030-verify-tag.sh: test_expect_success GPG 'verifying tag with --format' '
       	test_cmp expect actual
       '
       
     -+test_expect_success GPG 'verifying tag with --format="%(rest)" must failed' '
     ++test_expect_success GPG 'verifying tag with --format="%(rest)" must fail' '
      +	test_must_fail git verify-tag --format="%(rest)" "fourth-signed"
      +'
      +

5: 49063372e003 ! 6: 35a376db1fc1 [GSOC] ref-filter: teach get_object() return useful value
@@ Metadata
Author: ZheNing Hu <[email protected]>

## Commit message ##
- [GSOC] ref-filter: teach get_object() return useful value
+ [GSOC] ref-filter: pass get_object() return value to their callers

- Let populate_value(), get_ref_atom_value() and
- format_ref_array_item() get the return value of get_object()
- correctly. This can help us later let cat-file --batch get the
- correct error message and return value of get_object().
+ Since in the refactor of git cat-file --batch later,
+ oid_object_info_extended() in get_object() will be used to obtain
+ the info of an object with it's oid. When the object cannot be
+ obtained in the git repository, cat-file --batch expects to output
+ "<oid> missing" and continue the next oid query instead of letting
+ Git exit. In other error conditions, Git should exit normally. So we
+ can achieve this function by passing the return value of get_object().

+ Helped-by: Ævar Arnfjörð Bjarmason <[email protected]>

## ref-filter.c ##
-@@ ref-filter.c: static char *get_worktree_path(const struct used_atom *atom, const struct ref_ar
- static int populate_value(struct ref_array_item *ref, struct strbuf *err)
+@@ ref-filter.c: static int populate_value(struct ref_array_item *ref, struct strbuf *err)
{
struct object *obj;
-- int i;
-+ int i, ret = 0;
+ int i;
++ int ret = 0;
struct object_info empty = OBJECT_INFO_INIT;

CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ ref-filter.c: static int populate_value(struct ref_array_item *ref, struct strbu
oi.oid = ref->objectname;
- if (get_object(ref, 0, &obj, &oi, err))
- return -1;
-+ if ((ret = get_object(ref, 0, &obj, &oi, err)))
++ ret = get_object(ref, 0, &obj, &oi, err);
++ if (ret)
+ return ret;

/*
* If there is no atom that wants to know about tagged
-@@ ref-filter.c: static int populate_value(struct ref_array_item *ref, struct strbuf *err)
- static int get_ref_atom_value(struct ref_array_item *ref, int atom,
+@@ ref-filter.c: static int get_ref_atom_value(struct ref_array_item *ref, int atom,
struct atom_value **v, struct strbuf *err)
{
-+ int ret = 0;
-+
if (!ref->value) {
- if (populate_value(ref, err))
- return -1;
-+ if ((ret = populate_value(ref, err)))
++ int ret = populate_value(ref, err);
++
++ if (ret)
+ return ret;
fill_missing_values(ref->value);
}
@@ ref-filter.c: int format_ref_array_item(struct ref_array_item *info,
}
}
if (*cp) {
-@@ ref-filter.c: int format_ref_array_item(struct ref_array_item *info,
- }
- strbuf_addbuf(final_buf, &state.stack->output);
- pop_stack_element(&state.stack);
-- return 0;
-+ return ret;
- }
-
- void pretty_print_ref(const char *name, const struct object_id *oid,
6: d2f2563eb76a ! 7: 8c1d683ec6e9 [GSOC] ref-filter: introduce free_array_item_internal() function
@@ Metadata
Author: ZheNing Hu <[email protected]>

## Commit message ##
- [GSOC] ref-filter: introduce free_array_item_internal() function
+ [GSOC] ref-filter: introduce free_ref_array_item_value() function

- Introduce free_array_item_internal() for freeing ref_array_item value.
+ When we use ref_array_item which is not dynamically allocated and
+ want to free the space of its member "value" after the end of use,
+ free_array_item() does not meet our needs, because it tries to free
+ ref_array_item itself and its member "symref".
+
+ Introduce free_ref_array_item_value() for freeing ref_array_item value.
It will be called internally by free_array_item(), and it will help
- cat-file --batch free ref_array_item's memory later.
+ cat-file --batch free ref_array_item's value memory later.

+ Helped-by: Junio C Hamano <[email protected]>

@@ ref-filter.c: static int ref_filter_handler(const char *refname, const struct ob

-/* Free memory allocated for a ref_array_item */
-static void free_array_item(struct ref_array_item *item)
-+void free_array_item_internal(struct ref_array_item *item)
++void free_ref_array_item_value(struct ref_array_item *item)
{
- free((char *)item->symref);
if (item->value) {
@@ ref-filter.c: static int ref_filter_handler(const char *refname, const struct ob
+static void free_array_item(struct ref_array_item *item)
+{
+ free((char *)item->symref);
-+ free_array_item_internal(item);
++ free_ref_array_item_value(item);
free(item);
}

@@ ref-filter.h: struct ref_format {
int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type); /* Clear all memory allocated to ref_array */
void ref_array_clear(struct ref_array *array);
-+/* Free array item's value */
-+void free_array_item_internal(struct ref_array_item *item);
++/* Free ref_array_item's value */
++void free_ref_array_item_value(struct ref_array_item *item);
/* Used to verify if the given format is correct and to parse out the used atoms */ int verify_ref_format(struct ref_format *format);
/* Sort the given ref_array as per the ref_sorting provided */
7: 765337a46ab0 ! 8: bd534a266a40 [GSOC] cat-file: reuse ref-filter logic
@@ Commit message
verify_ref_format() to check atoms.
4. Use has_object_file() in batch_one_object() to check
whether the input object exists.
- 5. Use format_ref_array_item() in batch_object_write() to
+ 5. Let get_object() return 1 and print "<oid> missing" instead
+ of returning -1 and printing "missing object <oid> for <refname>",
+ this can help format_ref_array_item() just report that the
+ object is missing without letting Git exit.
+ 6. 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 less than zero, use die() to print the error message
and exit; else return value greater than zero, only print the
error message, but not exit.
- 6. Let get_object() return 1 and print "<oid> missing" instead
- of returning -1 and printing "missing object <oid> for <refname>",
- this can help format_ref_array_item() just report that the
- object is missing without letting Git exit.
+ 7. 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),
@@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi
+ printf("%s\n", err.buf);
+ fflush(stdout);
}
-+ free_array_item_internal(&item);
++ free_ref_array_item_value(&item);
+ strbuf_release(&err);
}

@@ builtin/cat-file.c: int cmd_cat_file(int argc, const char **argv, const char *pr

## ref-filter.c ##
@@ ref-filter.c: int verify_ref_format(struct ref_format *format)
- at = parse_ref_filter_atom(format, sp + 2, ep, &err);
if (at < 0)
die("%s", err.buf);
+
- if (used_atom[at].atom_type == ATOM_REST)
- die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+ if ((!format->cat_file_mode && used_atom[at].atom_type == ATOM_REST) ||
@@ ref-filter.c: int verify_ref_format(struct ref_format *format)
+ used_atom[at].atom_type == ATOM_WORKTREEPATH)))
+ die(_("this command reject atom %%(%.*s)"), (int)(ep - sp - 2), sp + 2);

- if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
- used_atom[at].u.raw_data.option == RAW_BARE)
+ if ((format->quote_style == QUOTE_PYTHON ||
+ format->quote_style == QUOTE_SHELL ||
@@ ref-filter.c: static int get_object(struct ref_array_item *ref, int deref, struct object **obj
}
if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
@@ t/t1006-cat-file.sh: test_expect_success 'cat-file --unordered works' '
+batch_test_atom() {
+ if test "$3" = "fail"
+ then
-+ test_expect_${4:-success} $PREREQ "basic atom: $1 $2 mast failed" "
++ test_expect_${4:-success} $PREREQ "basic atom: $1 $2 must fail" "
+ test_must_fail git cat-file --batch-check='$2' >bad <<-EOF
+ $1
+ EOF
8: 058b304686fd ! 9: b66ab0f2d569 [GSOC] cat-file: reuse err buf in batch_objet_write()
@@ Metadata
Author: ZheNing Hu <[email protected]>

## Commit message ##
- [GSOC] cat-file: reuse err buf in batch_objet_write()
+ [GSOC] cat-file: reuse err buf in batch_object_write()

Reuse the err buffer in batch_object_write(), as the
buffer scratch does. This will reduce the overhead
@@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi
+ printf("%s\n", err->buf);
fflush(stdout);
}
- free_array_item_internal(&item);
+ free_ref_array_item_value(&item);
- strbuf_release(&err);
}

  9:  cbf7d51933ea ! 10:  86ac3bcaecea [GSOC] cat-file: re-implement --textconv, --filters options
     @@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt, const st
       	opt->format.format = format.buf;
      +	if (opt->cmdmode == 'c')
      +		opt->format.use_textconv = 1;
     -+	if (opt->cmdmode == 'w')
     ++	else if (opt->cmdmode == 'w')
      +		opt->format.use_filters = 1;
      +
       	if (verify_ref_format(&opt->format))
     @@ ref-filter.c: int verify_ref_format(struct ref_format *format)
      +		use_filters = format->use_filters;
      +		use_textconv = format->use_textconv;
      +
     - 		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
     - 		    used_atom[at].u.raw_data.option == RAW_BARE)
     - 			die(_("--format=%.*s cannot be used with"
     + 		if ((format->quote_style == QUOTE_PYTHON ||
     + 		     format->quote_style == QUOTE_SHELL ||
     + 		     format->quote_style == QUOTE_TCL) &&
      @@ ref-filter.c: static int get_object(struct ref_array_item *ref, int deref, struct object **obj
       {
       	/* parse_object_buffer() will set eaten to 0 if free() will be needed */
     @@ ref-filter.c: static int get_object(struct ref_array_item *ref, int deref, struc
      +		oi->info.typep = NULL;
      +		oi->info.contentp = temp_contentp;
      +
     -+		if (use_textconv) {
     ++		if (use_textconv && !ref->rest)
     ++			return strbuf_addf_ret(err, -1, _("missing path for '%s'"),
     ++					       oid_to_hex(&act_oi.oid));
     ++		if (use_textconv && oi->type == OBJ_BLOB) {
      +			act_oi = *oi;
     -+
     -+			if(!ref->rest)
     -+				return strbuf_addf_ret(err, -1, _("missing path for '%s'"),
     -+						       oid_to_hex(&act_oi.oid));
     -+			if (act_oi.type == OBJ_BLOB) {
     -+				if (textconv_object(the_repository,
     -+						    ref->rest, 0100644, &act_oi.oid,
     -+						    1, (char **)(&act_oi.content), &act_oi.size)) {
     -+					actual_oi = &act_oi;
     -+					goto success;
     -+				}
     ++			if (textconv_object(the_repository,
     ++					    ref->rest, 0100644, &act_oi.oid,
     ++					    1, (char **)(&act_oi.content), &act_oi.size)) {
     ++				actual_oi = &act_oi;
     ++				goto success;
      +			}
      +		}
       	}
     @@ ref-filter.c: static int get_object(struct ref_array_item *ref, int deref, struc
       	if (oi->info.contentp) {
      -		*obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
     -+		if (use_filters) {
     -+			if(!ref->rest)
     -+				return strbuf_addf_ret(err, -1, _("missing path for '%s'"),
     -+						       oid_to_hex(&oi->oid));
     -+			if (oi->type == OBJ_BLOB) {
     -+				struct strbuf strbuf = STRBUF_INIT;
     -+				struct checkout_metadata meta;
     -+				act_oi = *oi;
     ++		if (use_filters && !ref->rest)
     ++			return strbuf_addf_ret(err, -1, _("missing path for '%s'"),
     ++					       oid_to_hex(&oi->oid));
     ++		if (use_filters && oi->type == OBJ_BLOB) {
     ++			struct strbuf strbuf = STRBUF_INIT;
     ++			struct checkout_metadata meta;
     ++			act_oi = *oi;
      +
     -+				init_checkout_metadata(&meta, NULL, NULL, &act_oi.oid);
     -+				if (convert_to_working_tree(&the_index, ref->rest, act_oi.content, act_oi.size, &strbuf, &meta)) {
     -+					act_oi.size = strbuf.len;
     -+					act_oi.content = strbuf_detach(&strbuf, NULL);
     -+					actual_oi = &act_oi;
     -+				} else {
     -+					die("could not convert '%s' %s",
     -+					    oid_to_hex(&oi->oid), ref->rest);
     -+				}
     -+			}
     ++			init_checkout_metadata(&meta, NULL, NULL, &act_oi.oid);
     ++			if (!convert_to_working_tree(&the_index, ref->rest, act_oi.content, act_oi.size, &strbuf, &meta))
     ++				die("could not convert '%s' %s",
     ++					oid_to_hex(&oi->oid), ref->rest);
     ++			act_oi.size = strbuf.len;
     ++			act_oi.content = strbuf_detach(&strbuf, NULL);
     ++			actual_oi = &act_oi;
      +		}
      +
      +success:
     @@ ref-filter.h: struct ref_format {
       };
       
      -#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
     -+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, 0, 0, -1 }
     ++#define REF_FORMAT_INIT { .use_color = -1 }
       
       /*  Macros for checking --merged and --no-merged options */
       #define _OPT_MERGED_NO_MERGED(option, filter, h) \
Signature · 1 line
gitgitgadget
#45 lore raw

PATCH v3 1/10 ref-filter: add obj-type check in grab contents

From: ZheNing Hu <[email protected]>

Only tag and commit objects use grab_sub_body_contents() to grab object contents in the current codebase. We want to teach the function to also handle blobs and trees to get their raw data, without parsing a blob (whose contents looks like a commit or a tag) incorrectly as a commit or a tag.

Skip the block of code that is specific to handling commits and tags early when the given object is of a wrong type to help later addition to handle other types of objects in this function.

---
 ref-filter.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)
ref-filter.c +15 -9
index 4db0e40ff4c6..5cee6512fbaf 100644
@@ -1356,11 +1356,12 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
 }
 
 /* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
 {
 	int i;
 	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
 	size_t sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+	void *buf = data->content;
 
 	for (i = 0; i < used_atom_cnt; i++) {
 		struct used_atom *atom = &used_atom[i];
@@ -1371,10 +1372,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
 			continue;
 		if (deref)
 			name++;
-		if (strcmp(name, "body") &&
-		    !starts_with(name, "subject") &&
-		    !starts_with(name, "trailers") &&
-		    !starts_with(name, "contents"))
+
+		if ((data->type != OBJ_TAG &&
+		     data->type != OBJ_COMMIT) ||
+		    (strcmp(name, "body") &&
+		     !starts_with(name, "subject") &&
+		     !starts_with(name, "trailers") &&
+		     !starts_with(name, "contents")))
 			continue;
 		if (!subpos)
 			find_subpos(buf,
@@ -1438,17 +1442,19 @@ static void fill_missing_values(struct atom_value *val)
  * pointed at by the ref itself; otherwise it is the object the
  * ref (which is a tag) refers to.
  */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
+static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
 {
+	void *buf = data->content;
+
 	switch (obj->type) {
 	case OBJ_TAG:
 		grab_tag_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("tagger", val, deref, buf);
 		break;
 	case OBJ_COMMIT:
 		grab_commit_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("author", val, deref, buf);
 		grab_person("committer", val, deref, buf);
 		break;
@@ -1678,7 +1684,7 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 			return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
 					       oid_to_hex(&oi->oid), ref->refname);
 		}
-		grab_values(ref->value, deref, *obj, oi->content);
+		grab_values(ref->value, deref, *obj, oi);
 	}
 
 	grab_common_values(ref->value, deref, oi);
Signature · 1 line
gitgitgadget
#46 lore raw

PATCH v3 2/10 ref-filter: add %(raw) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add new formatting option %(raw), which will print the raw object data without any changes. It will help further to migrate all cat-file formatting logic from cat-file to ref-filter.

The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).

E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.

Therefore, we need to find a way to record the length of the atom_value's member s. Although strbuf can already record the string and its length, if we want to replace the type of atom_value's member s with strbuf, many places in ref-filter that are filled with dynamically allocated mermory in v->s are not easy to replace. At the same time, we need to check if v->s == NULL in populate_value(), and strbuf cannot easily distinguish NULL and empty strings, but c-style "const char *" can do it. So add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

---
 Documentation/git-for-each-ref.txt |   9 ++
 ref-filter.c                       | 139 +++++++++++++++----
 t/t6300-for-each-ref.sh            | 216 +++++++++++++++++++++++++++++
 3 files changed, 337 insertions(+), 27 deletions(-)
Documentation/git-for-each-ref.txt +9 -0
index 2ae2478de706..7f1f0a1ca3b6 100644
@@ -235,6 +235,15 @@ and `date` to extract the named component.  For email fields (`authoremail`,
 without angle brackets, and `:localpart` to get the part before the `@` symbol
 out of the trimmed email.
 
+The raw data in an object is `raw`.
+
+raw:size::
+	The raw data size of the object.
+
+Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
+`--perl` because the host language may not support arbitrary binary data in the
+variables of its string type.
+
 The message in a commit or a tag object is `contents`, from which
 `contents:<part>` can be used to extract various parts out of:
 
ref-filter.c +112 -27

293 lines not on this page · raw message

t/t6300-for-each-ref.sh +216 -0

258 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#47 lore raw

PATCH v3 3/10 ref-filter: --format=%(raw) re-support --perl

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because the perl language can handle binary data correctly, add the function perl_quote_buf_with_len(), which can specify the length of the data and prevent the data from being truncated at '\0' to help --format="%(raw)" re-support --perl.

---
 Documentation/git-for-each-ref.txt |  2 +-
 quote.c                            | 17 +++++++++++++++++
 quote.h                            |  1 +
 ref-filter.c                       | 15 +++++++++++----
 t/t6300-for-each-ref.sh            | 19 +++++++++++++++++--
 5 files changed, 47 insertions(+), 7 deletions(-)
Documentation/git-for-each-ref.txt +1 -1
index 7f1f0a1ca3b6..ea9b438c16f6 100644
@@ -241,7 +241,7 @@ raw:size::
 	The raw data size of the object.
 
 Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
-`--perl` because the host language may not support arbitrary binary data in the
+because the host language may not support arbitrary binary data in the
 variables of its string type.
 
 The message in a commit or a tag object is `contents`, from which
quote.c +17 -0
index 8a3a5e39eb12..26719d21d1e7 100644
@@ -471,6 +471,23 @@ void perl_quote_buf(struct strbuf *sb, const char *src)
 	strbuf_addch(sb, sq);
 }
 
+void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len)
+{
+	const char sq = '\'';
+	const char bq = '\\';
+	const char *c = src;
+	const char *end = src + len;
+
+	strbuf_addch(sb, sq);
+	while (c != end) {
+		if (*c == sq || *c == bq)
+			strbuf_addch(sb, bq);
+		strbuf_addch(sb, *c);
+		c++;
+	}
+	strbuf_addch(sb, sq);
+}
+
 void python_quote_buf(struct strbuf *sb, const char *src)
 {
 	const char sq = '\'';
quote.h +1 -0
index 768cc6338e27..0fe69e264b0e 100644
@@ -94,6 +94,7 @@ char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigne
 
 /* quoting as a string literal for other languages */
 void perl_quote_buf(struct strbuf *sb, const char *src);
+void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len);
 void python_quote_buf(struct strbuf *sb, const char *src);
 void tcl_quote_buf(struct strbuf *sb, const char *src);
 void basic_regex_quote_buf(struct strbuf *sb, const char *src);
ref-filter.c +11 -4
index 7822be903071..797b20ffa612 100644
@@ -742,7 +742,10 @@ static void quote_formatting(struct strbuf *s, const char *str, size_t len, int
 		sq_quote_buf(s, str);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(s, str);
+		if (len != ATOM_VALUE_S_SIZE_INIT)
+			perl_quote_buf_with_len(s, str, len);
+		else
+			perl_quote_buf(s, str);
 		break;
 	case QUOTE_PYTHON:
 		python_quote_buf(s, str);
@@ -1006,10 +1009,14 @@ int verify_ref_format(struct ref_format *format)
 		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
 		if (at < 0)
 			die("%s", err.buf);
-		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
-		    used_atom[at].u.raw_data.option == RAW_BARE)
+
+		if ((format->quote_style == QUOTE_PYTHON ||
+		     format->quote_style == QUOTE_SHELL ||
+		     format->quote_style == QUOTE_TCL) &&
+		     used_atom[at].atom_type == ATOM_RAW &&
+		     used_atom[at].u.raw_data.option == RAW_BARE)
 			die(_("--format=%.*s cannot be used with"
-			      "--python, --shell, --tcl, --perl"), (int)(ep - sp - 2), sp + 2);
+			      "--python, --shell, --tcl"), (int)(ep - sp - 2), sp + 2);
 		cp = ep + 1;
 
 		if (skip_prefix(used_atom[at].name, "color:", &color))
t/t6300-for-each-ref.sh +17 -2
index 9c5379e2f56f..5556063c347d 100755
@@ -915,8 +915,23 @@ test_expect_success '%(raw) with --tcl must fail' '
 	test_must_fail git for-each-ref --format="%(raw)" --tcl
 '
 
-test_expect_success '%(raw) with --perl must fail' '
-	test_must_fail git for-each-ref --format="%(raw)" --perl
+test_expect_success '%(raw) with --perl' '
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob1 --perl | perl > actual &&
+	cmp blob1 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob3 --perl | perl > actual &&
+	cmp blob3 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob8 --perl | perl > actual &&
+	cmp blob8 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/first --perl | perl > actual &&
+	cmp one actual &&
+	git cat-file tree refs/mytrees/first > expected &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/mytrees/first --perl | perl > actual &&
+	cmp expected actual
 '
 
 test_expect_success '%(raw) with --shell must fail' '
Signature · 1 line
gitgitgadget
#48 lore raw

PATCH v3 4/10 ref-filter: use non-const ref_format in *_atom_parser()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Use non-const ref_format in *_atom_parser(), which can help us modify the members of ref_format in *_atom_parser().

---
 builtin/tag.c |  2 +-
 ref-filter.c  | 44 ++++++++++++++++++++++----------------------
 ref-filter.h  |  4 ++--
 3 files changed, 25 insertions(+), 25 deletions(-)
builtin/tag.c +1 -1
index 82fcfc098242..452558ec9575 100644
@@ -146,7 +146,7 @@ static int verify_tag(const char *name, const char *ref,
 		      const struct object_id *oid, void *cb_data)
 {
 	int flags;
-	const struct ref_format *format = cb_data;
+	struct ref_format *format = cb_data;
 	flags = GPG_VERIFY_VERBOSE;
 
 	if (format->format)
ref-filter.c +22 -22

164 lines not on this page · raw message

ref-filter.h +2 -2
index baf72a718965..74fb423fc89f 100644
@@ -116,7 +116,7 @@ void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
 void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, unsigned int mask, int on);
 /*  Based on the given format and quote_style, fill the strbuf */
 int format_ref_array_item(struct ref_array_item *info,
-			  const struct ref_format *format,
+			  struct ref_format *format,
 			  struct strbuf *final_buf,
 			  struct strbuf *error_buf);
 /*  Parse a single sort specifier and add it to the list */
@@ -137,7 +137,7 @@ void setup_ref_filter_porcelain_msg(void);
  * name must be a fully qualified refname.
  */
 void pretty_print_ref(const char *name, const struct object_id *oid,
-		      const struct ref_format *format);
+		      struct ref_format *format);
 
 /*
  * Push a single ref onto the array; this can be used to construct your own
Signature · 1 line
gitgitgadget
#49 lore raw

PATCH v3 5/10 ref-filter: add %(rest) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let "cat-file --batch=%(rest)" use the ref-filter interface, add %(rest) atom for ref-filter. "git for-each-ref", "git branch", "git tag" and "git verify-tag" will reject %(rest) by default.

---
 ref-filter.c             | 21 +++++++++++++++++++++
 ref-filter.h             |  5 ++++-
 t/t3203-branch-output.sh |  4 ++++
 t/t6300-for-each-ref.sh  |  4 ++++
 t/t7004-tag.sh           |  4 ++++
 t/t7030-verify-tag.sh    |  4 ++++
 6 files changed, 41 insertions(+), 1 deletion(-)
ref-filter.c +21 -0
index d01a0266fb89..10c78de9cfa4 100644
@@ -157,6 +157,7 @@ enum atom_type {
 	ATOM_IF,
 	ATOM_THEN,
 	ATOM_ELSE,
+	ATOM_REST,
 };
 
 /*
@@ -559,6 +560,15 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
 	return 0;
 }
 
+static int rest_atom_parser(struct ref_format *format, struct used_atom *atom,
+			    const char *arg, struct strbuf *err)
+{
+	if (arg)
+		return strbuf_addf_ret(err, -1, _("%%(rest) does not take arguments"));
+	format->use_rest = 1;
+	return 0;
+}
+
 static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
 			    const char *arg, struct strbuf *unused_err)
 {
@@ -615,6 +625,7 @@ static struct {
 	[ATOM_IF] = { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
 	[ATOM_THEN] = { "then", SOURCE_NONE },
 	[ATOM_ELSE] = { "else", SOURCE_NONE },
+	[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
 	/*
 	 * Please update $__git_ref_fieldlist in git-completion.bash
 	 * when you add new atoms
@@ -1010,6 +1021,9 @@ int verify_ref_format(struct ref_format *format)
 		if (at < 0)
 			die("%s", err.buf);
 
+		if (used_atom[at].atom_type == ATOM_REST)
+			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+
 		if ((format->quote_style == QUOTE_PYTHON ||
 		     format->quote_style == QUOTE_SHELL ||
 		     format->quote_style == QUOTE_TCL) &&
@@ -1927,6 +1941,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 			v->handler = else_atom_handler;
 			v->s = xstrdup("");
 			continue;
+		} else if (atom_type == ATOM_REST) {
+			if (ref->rest)
+				v->s = xstrdup(ref->rest);
+			else
+				v->s = xstrdup("");
+			continue;
 		} else
 			continue;
 
@@ -2144,6 +2164,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
 
 	FLEX_ALLOC_STR(ref, refname, refname);
 	oidcpy(&ref->objectname, oid);
+	ref->rest = NULL;
 
 	return ref;
 }
ref-filter.h +4 -1
index 74fb423fc89f..9dc07476a584 100644
@@ -38,6 +38,7 @@ struct ref_sorting {
 
 struct ref_array_item {
 	struct object_id objectname;
+	const char *rest;
 	int flag;
 	unsigned int kind;
 	const char *symref;
@@ -76,14 +77,16 @@ struct ref_format {
 	 * verify_ref_format() afterwards to finalize.
 	 */
 	const char *format;
+	const char *rest;
 	int quote_style;
+	int use_rest;
 	int use_color;
 
 	/* Internal state to ref-filter */
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
t/t3203-branch-output.sh +4 -0
index 5325b9f67a00..6e94c6db7b5a 100755
@@ -340,6 +340,10 @@ test_expect_success 'git branch --format option' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch with --format=%(rest) must fail' '
+	test_must_fail git branch --format="%(rest)" >actual
+'
+
 test_expect_success 'worktree colors correct' '
 	cat >expect <<-EOF &&
 	* <GREEN>(HEAD detached from fromtag)<RESET>
t/t6300-for-each-ref.sh +4 -0
index 5556063c347d..82c0ad2cb115 100755
@@ -1211,6 +1211,10 @@ test_expect_success 'basic atom: head contents:trailers' '
 	test_cmp expect actual.clean
 '
 
+test_expect_success 'basic atom: rest must fail' '
+	test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
+'
+
 test_expect_success 'trailer parsing not fooled by --- line' '
 	git commit --allow-empty -F - <<-\EOF &&
 	this is the subject
t/t7004-tag.sh +4 -0
index 2f72c5c6883e..082be85dffc7 100755
@@ -1998,6 +1998,10 @@ test_expect_success '--format should list tags as per format given' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git tag -l with --format="%(rest)" must fail' '
+	test_must_fail git tag -l --format="%(rest)" "v1*"
+'
+
 test_expect_success "set up color tests" '
 	echo "<RED>v1.0<RESET>" >expect.color &&
 	echo "v1.0" >expect.bare &&
t/t7030-verify-tag.sh +4 -0
index 3cefde9602bf..10faa645157e 100755
@@ -194,6 +194,10 @@ test_expect_success GPG 'verifying tag with --format' '
 	test_cmp expect actual
 '
 
+test_expect_success GPG 'verifying tag with --format="%(rest)" must fail' '
+	test_must_fail git verify-tag --format="%(rest)" "fourth-signed"
+'
+
 test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
 	test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
 	test_must_be_empty actual-forged
Signature · 1 line
gitgitgadget
#50 lore raw

PATCH v3 6/10 ref-filter: pass get_object() return value to their callers

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Since in the refactor of git cat-file --batch later, oid_object_info_extended() in get_object() will be used to obtain the info of an object with it's oid. When the object cannot be obtained in the git repository, cat-file --batch expects to output "<oid> missing" and continue the next oid query instead of letting Git exit. In other error conditions, Git should exit normally. So we can achieve this function by passing the return value of get_object().

---
 ref-filter.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)
ref-filter.c +11 -6
index 10c78de9cfa4..58def6ccd33a 100644
@@ -1816,6 +1816,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
 	struct object *obj;
 	int i;
+	int ret = 0;
 	struct object_info empty = OBJECT_INFO_INIT;
 
 	CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -1972,8 +1973,9 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
 
 	oi.oid = ref->objectname;
-	if (get_object(ref, 0, &obj, &oi, err))
-		return -1;
+	ret = get_object(ref, 0, &obj, &oi, err);
+	if (ret)
+		return ret;
 
 	/*
 	 * If there is no atom that wants to know about tagged
@@ -2005,8 +2007,10 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom,
 			      struct atom_value **v, struct strbuf *err)
 {
 	if (!ref->value) {
-		if (populate_value(ref, err))
-			return -1;
+		int ret = populate_value(ref, err);
+
+		if (ret)
+			return ret;
 		fill_missing_values(ref->value);
 	}
 	*v = &ref->value[atom];
@@ -2580,6 +2584,7 @@ int format_ref_array_item(struct ref_array_item *info,
 {
 	const char *cp, *sp, *ep;
 	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+	int ret = 0;
 
 	state.quote_style = format->quote_style;
 	push_stack_element(&state.stack);
@@ -2592,10 +2597,10 @@ int format_ref_array_item(struct ref_array_item *info,
 		if (cp < sp)
 			append_literal(cp, sp, &state);
 		pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-		if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+		if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||
 		    atomv->handler(atomv, &state, error_buf)) {
 			pop_stack_element(&state.stack);
-			return -1;
+			return ret ? ret : -1;
 		}
 	}
 	if (*cp) {
Signature · 1 line
gitgitgadget
#51 lore raw

PATCH v3 7/10 ref-filter: introduce free_ref_array_item_value() function

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

When we use ref_array_item which is not dynamically allocated and want to free the space of its member "value" after the end of use, free_array_item() does not meet our needs, because it tries to free ref_array_item itself and its member "symref".

Introduce free_ref_array_item_value() for freeing ref_array_item value. It will be called internally by free_array_item(), and it will help cat-file --batch free ref_array_item's value memory later.

---
 ref-filter.c | 11 ++++++++---
 ref-filter.h |  2 ++
 2 files changed, 10 insertions(+), 3 deletions(-)
ref-filter.c +8 -3
index 58def6ccd33a..22315d4809dc 100644
@@ -2291,16 +2291,21 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 	return 0;
 }
 
-/*  Free memory allocated for a ref_array_item */
-static void free_array_item(struct ref_array_item *item)
+void free_ref_array_item_value(struct ref_array_item *item)
 {
-	free((char *)item->symref);
 	if (item->value) {
 		int i;
 		for (i = 0; i < used_atom_cnt; i++)
 			free((char *)item->value[i].s);
 		free(item->value);
 	}
+}
+
+/*  Free memory allocated for a ref_array_item */
+static void free_array_item(struct ref_array_item *item)
+{
+	free((char *)item->symref);
+	free_ref_array_item_value(item);
 	free(item);
 }
 
ref-filter.h +2 -0
index 9dc07476a584..76f9af7b4676 100644
@@ -111,6 +111,8 @@ struct ref_format {
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
 /*  Clear all memory allocated to ref_array */
 void ref_array_clear(struct ref_array *array);
+/* Free ref_array_item's value */
+void free_ref_array_item_value(struct ref_array_item *item);
 /*  Used to verify if the given format is correct and to parse out the used atoms */
 int verify_ref_format(struct ref_format *format);
 /*  Sort the given ref_array as per the ref_sorting provided */
Signature · 1 line
gitgitgadget
#52 lore raw

PATCH v3 8/10 cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let cat-file use ref-filter logic, the following methods are used:

1. Add cat_file_mode member in struct ref_format, this can help us reject atoms in verify_ref_format() which cat-file cannot use, e.g. %(refname), %(push), %(upstream)...
2. Change the type of member format in struct batch_options to ref_format, We can add format data in it.
3. Let batch_objects() add atoms to format, and use verify_ref_format() to check atoms.
4. Use has_object_file() in batch_one_object() to check whether the input object exists.
5. Let get_object() return 1 and print "<oid> missing" instead of returning -1 and printing "missing object <oid> for <refname>", this can help format_ref_array_item() just report that the object is missing without letting Git exit.
6. 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 less than zero, use die() to print the error message and exit; else return value greater than zero, only print the error message, but not exit.
7. 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 refname.

---
 Documentation/git-cat-file.txt |   6 +
 builtin/cat-file.c             | 248 +++++++-------------------------
 ref-filter.c                   |  15 +-
 ref-filter.h                   |   3 +-
 t/t1006-cat-file.sh            | 252 +++++++++++++++++++++++++++++++++
 t/t6301-for-each-ref-errors.sh |   2 +-
 6 files changed, 323 insertions(+), 203 deletions(-)
Documentation/git-cat-file.txt +6 -0
index 4eb0421b3fd9..ef8ab952b2fa 100644
@@ -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)`.
 
builtin/cat-file.c +51 -197

349 lines not on this page · raw message

ref-filter.c +11 -4
index 22315d4809dc..181d99c92735 100644
@@ -1021,8 +1021,15 @@ int verify_ref_format(struct ref_format *format)
 		if (at < 0)
 			die("%s", err.buf);
 
-		if (used_atom[at].atom_type == ATOM_REST)
-			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+		if ((!format->cat_file_mode && used_atom[at].atom_type == ATOM_REST) ||
+		    (format->cat_file_mode && (used_atom[at].atom_type == ATOM_FLAG ||
+					       used_atom[at].atom_type == ATOM_HEAD ||
+					       used_atom[at].atom_type == ATOM_PUSH ||
+					       used_atom[at].atom_type == ATOM_REFNAME ||
+					       used_atom[at].atom_type == ATOM_SYMREF ||
+					       used_atom[at].atom_type == ATOM_UPSTREAM ||
+					       used_atom[at].atom_type == ATOM_WORKTREEPATH)))
+			die(_("this command reject atom %%(%.*s)"), (int)(ep - sp - 2), sp + 2);
 
 		if ((format->quote_style == QUOTE_PYTHON ||
 		     format->quote_style == QUOTE_SHELL ||
@@ -1742,8 +1749,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 	}
 	if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
 				     OBJECT_INFO_LOOKUP_REPLACE))
-		return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
-				       oid_to_hex(&oi->oid), ref->refname);
+		return strbuf_addf_ret(err, 1, _("%s missing"),
+				       oid_to_hex(&oi->oid));
 	if (oi->info.disk_sizep && oi->disk_size < 0)
 		BUG("Object size is less than zero.");
 
ref-filter.h +2 -1
index 76f9af7b4676..e5fe26e3fdf9 100644
@@ -78,6 +78,7 @@ struct ref_format {
 	 */
 	const char *format;
 	const char *rest;
+	int cat_file_mode;
 	int quote_style;
 	int use_rest;
 	int use_color;
@@ -86,7 +87,7 @@ struct ref_format {
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
t/t1006-cat-file.sh +252 -0

256 lines not on this page · raw message

t/t6301-for-each-ref-errors.sh +1 -1
index 40edf9dab534..3553f84a00c1 100755
@@ -41,7 +41,7 @@ test_expect_success 'Missing objects are reported correctly' '
 	r=refs/heads/missing &&
 	echo $MISSING >.git/$r &&
 	test_when_finished "rm -f .git/$r" &&
-	echo "fatal: missing object $MISSING for $r" >missing-err &&
+	echo "fatal: $MISSING missing" >missing-err &&
 	test_must_fail git for-each-ref 2>err &&
 	test_cmp missing-err err &&
 	(
Signature · 1 line
gitgitgadget
#53 lore raw
Quoted 5 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let cat-file use ref-filter logic, the following methods are used:

Maybe: s/the following methods are used/let's do the following/

Quoted 5 lines

1. Add cat_file_mode member in struct ref_format, this can help us reject atoms in verify_ref_format() which cat-file cannot use, e.g. %(refname), %(push), %(upstream)...
2. Change the type of member format in struct batch_options to ref_format, We can add format data in it.

Not sure what "We can add format data in it." means.

Quoted 6 lines

3. Let batch_objects() add atoms to format, and use verify_ref_format() to check atoms.
4. Use has_object_file() in batch_one_object() to check whether the input object exists.
5. Let get_object() return 1 and print "<oid> missing" instead of returning -1 and printing "missing object <oid> for <refname>",

Quoted 8 lines

this can help format_ref_array_item() just report that the object is missing without letting Git exit.
6. 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 less than zero, use die() to print the error message and exit; else return value greater than zero, only print the

s/else return value greater/else if the return value is greater/

Quoted 1 line

error message, but not exit.

s/not exit/don't exit/

Quoted 2 lines

7. Use free_ref_array_item_value() to free ref_array_item's value.

That looks like a lot of changes in a single commit. I wonder if this commit could be split.

Quoted 5 lines

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 refname.

s/refname/a refname/

It might be worth talking a bit about possible performance changes.

[...]

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 229

Quoted 6 lines
builtin/cat-file.c +6 -0
+       ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+       if (!ret) {
+               strbuf_addch(scratch, '\n');
+               batch_write(opt, scratch->buf, scratch->len);
+       } else if (ret < 0) {
+               die("%s\n", err.buf);

This if (ret < 0) could be checked first.

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 231

Quoted 2 lines
builtin/cat-file.c +2 -0
+       } else {
+               /* when ret > 0 , don't call die and print the err to stdout*/

I think it would be more helpful to tell what ret > 0 means, rather than what we do below (which can easily be seen).

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 234

Quoted 3 lines
builtin/cat-file.c +2 -0
+               printf("%s\n", err.buf);
+               fflush(stdout);
        }

For example:

       if (ret < 0) {
               die("%s\n", err.buf);
       if (ret) {
               /* ret > 0 means ... */
               printf("%s\n", err.buf);
               fflush(stdout);
       } else {
               strbuf_addch(scratch, '\n');
               batch_write(opt, scratch->buf, scratch->len);
       }
Quoted 3 lines
builtin/cat-file.c +2 -0
+       free_ref_array_item_value(&item);
+       strbuf_release(&err);
 }

[...]

Quoted 1 line

+static int batch_objects(struct batch_options *opt, const struct option *options)

It's unfortunate that one argument is called "opt" and the other one "options". I wonder if the first one could be called "batch" as it seems to be called this way somewhere else.

builtin/cat-file.c static int batch_unordered_packed(const struct object_id *oid, · line 373

Quoted 4 lines
builtin/cat-file.c +4 -0
+       if (!opt->format.format)
+               strbuf_addstr(&format, "%(objectname) %(objecttype) %(objectsize)");
+       else
+               strbuf_addstr(&format, opt->format.format);

If there is no reason for the condition to be (!X) over just (X), I think the latter is a bit better.

builtin/cat-file.c static int batch_unordered_packed(const struct object_id *oid, · line 376

Quoted 4 lines
        if (opt->print_contents)
builtin/cat-file.c +2 -1
-               data.info.typep = &data.type;
+               strbuf_addstr(&format, "\n%(raw)");
+       opt->format.format = format.buf;

I wonder if this should be:

      opt->format.format = strbuf_detach(&format, NULL);
Quoted 2 lines
builtin/cat-file.c +2 -0
+       if (verify_ref_format(&opt->format))
+               usage_with_options(cat_file_usage, options);

[...]

ref-filter.h struct ref_format { · line 90

Quoted 6 lines
ref-filter.h +1 -1
@@ -86,7 +87,7 @@ struct ref_format {
        int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }

Maybe this can already be changed to a designated initializer, like Ævar suggested recently.

[...]

t/t1006-cat-file.sh test_expect_success 'cat-file --unordered works' ' · line 806

Quoted 1 line
t/t1006-cat-file.sh +1 -0
+test_expect_success 'cat-file refs/heads/main refs/tags/testtag %(rest)' '

If this test is about checking that %(rest) works with both a branch and a tag, it might be better to say it more explicitly.

Quoted 10 lines
t/t1006-cat-file.sh +10 -0
+       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
+'
#54 lore raw
Quoted 8 lines · Christian Couder <[email protected]> 于2021年6月21日周一 下午1:55写道:
Quoted 6 lines

1. Add cat_file_mode member in struct ref_format, this can help us reject atoms in verify_ref_format() which cat-file cannot use, e.g. %(refname), %(push), %(upstream)...
2. Change the type of member format in struct batch_options to ref_format, We can add format data in it.

Not sure what "We can add format data in it." means.

Well, there is something wrong with the expression here. What I want to express is that we can fill its member "format" with the atoms like "%(objectname) %(refname)", and then pass it to the ref-filter.

Quoted 6 lines
Quoted 2 lines

7. Use free_ref_array_item_value() to free ref_array_item's value.

That looks like a lot of changes in a single commit. I wonder if this commit could be split.

Yeah, But I don’t know if I should take it apart step by step, If taken apart, those intermediate commits are likely to fail the test.

Quoted 10 lines
Quoted 5 lines

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 refname.

s/refname/a refname/

It might be worth talking a bit about possible performance changes.

Makes sense. The performance has indeed deteriorated.

Quoted 10 lines

[...]

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 229

Quoted 6 lines
builtin/cat-file.c +6 -0
+       ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+       if (!ret) {
+               strbuf_addch(scratch, '\n');
+               batch_write(opt, scratch->buf, scratch->len);
+       } else if (ret < 0) {
+               die("%s\n", err.buf);

This if (ret < 0) could be checked first.

Yes, it is better to put error checking first.

Quoted 7 lines

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 231

Quoted 3 lines
builtin/cat-file.c +2 -0
+       } else {
+               /* when ret > 0 , don't call die and print the err to stdout*/

I think it would be more helpful to tell what ret > 0 means, rather than what we do below (which can easily be seen).

Ah, There is indeed only one situation for ret > 0 for the time being:
Show "<oid> missing" without exiting Git.

Quoted 6 lines

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 234

Quoted 3 lines
builtin/cat-file.c +2 -0
+               printf("%s\n", err.buf);
+               fflush(stdout);
        }

For example:

Quoted 11 lines
       if (ret < 0) {
               die("%s\n", err.buf);
       if (ret) {
               /* ret > 0 means ... */
               printf("%s\n", err.buf);
               fflush(stdout);
       } else {
               strbuf_addch(scratch, '\n');
               batch_write(opt, scratch->buf, scratch->len);
       }

Yes, this might be better.

Quoted 4 lines

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 237

Quoted 3 lines
builtin/cat-file.c +2 -0
+       free_ref_array_item_value(&item);
+       strbuf_release(&err);
 }
Quoted 8 lines

[...]

Quoted 1 line

+static int batch_objects(struct batch_options *opt, const struct option *options)

It's unfortunate that one argument is called "opt" and the other one "options". I wonder if the first one could be called "batch" as it seems to be called this way somewhere else.

OK.

Quoted 8 lines

builtin/cat-file.c static int batch_unordered_packed(const struct object_id *oid, · line 373

Quoted 4 lines
builtin/cat-file.c +4 -0
+       if (!opt->format.format)
+               strbuf_addstr(&format, "%(objectname) %(objecttype) %(objectsize)");
+       else
+               strbuf_addstr(&format, opt->format.format);

If there is no reason for the condition to be (!X) over just (X), I think the latter is a bit better.

Although I think it doesn’t matter which one to use first, I will still follow your suggestions.

Quoted 9 lines

builtin/cat-file.c static int batch_unordered_packed(const struct object_id *oid, · line 376

Quoted 4 lines
        if (opt->print_contents)
builtin/cat-file.c +2 -1
-               data.info.typep = &data.type;
+               strbuf_addstr(&format, "\n%(raw)");
+       opt->format.format = format.buf;

I wonder if this should be:

      opt->format.format = strbuf_detach(&format, NULL);

No. here our opt->format.format will not be changed, it would be better for us to use strbuf_release(&format) for resource recovery. (strbuf_detach() will forced to let us free opt->format.format)

Quoted 9 lines
Quoted 2 lines
builtin/cat-file.c +2 -0
+       if (verify_ref_format(&opt->format))
+               usage_with_options(cat_file_usage, options);

[...]

Quoted 4 lines
ref-filter.h +0 -0
@@ -86,7 +87,7 @@ struct ref_format {
        int need_color_reset_at_eol;
 };
 
Quoted 6 lines

ref-filter.h struct ref_format { · line 90

Quoted 2 lines
ref-filter.h +1 -1
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }

Maybe this can already be changed to a designated initializer, like Ævar suggested recently.

I agree.

Quoted 7 lines

[...]

Quoted 1 line

+test_expect_success 'cat-file refs/heads/main refs/tags/testtag %(rest)' '

If this test is about checking that %(rest) works with both a branch and a tag, it might be better to say it more explicitly.

OK.

Thanks.
--
ZheNing Hu

#55 lore raw

PATCH v3 9/10 cat-file: reuse err buf in batch_object_write()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Reuse the err buffer in batch_object_write(), as the buffer scratch does. This will reduce the overhead of multiple allocations of memory of the err buffer.

---
 builtin/cat-file.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
builtin/cat-file.c +14 -8
index cbedb8e1c471..b5204493bd56 100644
@@ -212,32 +212,33 @@ static void batch_write(struct batch_options *opt, const void *data, int len)
 
 static void batch_object_write(const char *obj_name,
 			       struct strbuf *scratch,
+			       struct strbuf *err,
 			       struct batch_options *opt,
 			       struct expand_data *data)
 {
 	int ret = 0;
-	struct strbuf err = STRBUF_INIT;
 	struct ref_array_item item = { data->oid, data->rest };
 
 	strbuf_reset(scratch);
+	strbuf_reset(err);
 
-	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+	ret = format_ref_array_item(&item, &opt->format, scratch, err);
 	if (!ret) {
 		strbuf_addch(scratch, '\n');
 		batch_write(opt, scratch->buf, scratch->len);
 	} else if (ret < 0) {
-		die("%s\n", err.buf);
+		die("%s\n", err->buf);
 	} else {
 		/* when ret > 0 , don't call die and print the err to stdout*/
-		printf("%s\n", err.buf);
+		printf("%s\n", err->buf);
 		fflush(stdout);
 	}
 	free_ref_array_item_value(&item);
-	strbuf_release(&err);
 }
 
 static void batch_one_object(const char *obj_name,
 			     struct strbuf *scratch,
+			     struct strbuf *err,
 			     struct batch_options *opt,
 			     struct expand_data *data)
 {
@@ -291,7 +292,7 @@ static void batch_one_object(const char *obj_name,
 		return;
 	}
 
-	batch_object_write(obj_name, scratch, opt, data);
+	batch_object_write(obj_name, scratch, err, opt, data);
 }
 
 struct object_cb_data {
@@ -299,13 +300,14 @@ struct object_cb_data {
 	struct expand_data *expand;
 	struct oidset *seen;
 	struct strbuf *scratch;
+	struct strbuf *err;
 };
 
 static int batch_object_cb(const struct object_id *oid, void *vdata)
 {
 	struct object_cb_data *data = vdata;
 	oidcpy(&data->expand->oid, oid);
-	batch_object_write(NULL, data->scratch, data->opt, data->expand);
+	batch_object_write(NULL, data->scratch, data->err, data->opt, data->expand);
 	return 0;
 }
 
@@ -361,6 +363,7 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
+	struct strbuf err = STRBUF_INIT;
 	struct strbuf format = STRBUF_INIT;
 	struct expand_data data;
 	int save_warning;
@@ -389,6 +392,7 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 		cb.opt = opt;
 		cb.expand = &data;
 		cb.scratch = &output;
+		cb.err = &err;
 
 		if (opt->unordered) {
 			struct oidset seen = OIDSET_INIT;
@@ -413,6 +417,7 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 
 		strbuf_release(&format);
 		strbuf_release(&output);
+		strbuf_release(&err);
 		return 0;
 	}
 
@@ -441,12 +446,13 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 			data.rest = p;
 		}
 
-		batch_one_object(input.buf, &output, opt, &data);
+		batch_one_object(input.buf, &output, &err, opt, &data);
 	}
 
 	strbuf_release(&format);
 	strbuf_release(&input);
 	strbuf_release(&output);
+	strbuf_release(&err);
 	warn_on_object_refname_ambiguity = save_warning;
 	return retval;
 }
Signature · 1 line
gitgitgadget
#56 lore raw

PATCH v3 10/10 cat-file: re-implement --textconv, --filters options

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

After cat-file reuses the ref-filter logic, we re-implement the functions of --textconv and --filters options.

Add members use_textconv and use_filters in struct ref_format, and use global variables use_filters and use_textconv in ref-filter.c, so that we can filter the content of the object in get_object(). Use actual_oi to record the real expand_data:
it may point to the original oi or the act_oi processed by textconv_object() or convert_to_working_tree(). grab_values() will grab the contents of actual_oi and grab_common_values() to grab the contents of origin oi, this ensures that %(objectsize) still uses the size of the unfiltered data.

In get_object(), we made an optimization: Firstly, get the size and type of the object instead of directly getting the object data. If using --textconv, after successfully obtaining the filtered object data, an extra oid_object_info_extended() will be skipped, which can reduce the cost of object data copy; If using --filter, the data of the object first will be getted first, and then convert_to_working_tree() will be used to get the filtered object data.

---
 builtin/cat-file.c |  5 ++++
 ref-filter.c       | 59 ++++++++++++++++++++++++++++++++++++++++++++--
 ref-filter.h       |  4 +++-
 3 files changed, 65 insertions(+), 3 deletions(-)
builtin/cat-file.c +5 -0
index b5204493bd56..a78cc076aa59 100644
@@ -377,6 +377,11 @@ static int batch_objects(struct batch_options *opt, const struct option *options
 	if (opt->print_contents)
 		strbuf_addstr(&format, "\n%(raw)");
 	opt->format.format = format.buf;
+	if (opt->cmdmode == 'c')
+		opt->format.use_textconv = 1;
+	else if (opt->cmdmode == 'w')
+		opt->format.use_filters = 1;
+
 	if (verify_ref_format(&opt->format))
 		usage_with_options(cat_file_usage, options);
 
ref-filter.c +57 -2

101 lines not on this page · raw message

ref-filter.h +3 -1
index e5fe26e3fdf9..497e3e93632f 100644
@@ -80,6 +80,8 @@ struct ref_format {
 	const char *rest;
 	int cat_file_mode;
 	int quote_style;
+	int use_textconv;
+	int use_filters;
 	int use_rest;
 	int use_color;
 
@@ -87,7 +89,7 @@ struct ref_format {
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
+#define REF_FORMAT_INIT { .use_color = -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
Signature · 1 line
gitgitgadget
#57 lore raw

v4 cover cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic.

Change from last version:

1. At the suggestion of Christian Couder, split "[GSOC] cat-file: reuse ref-filter logic" into multiple small commits: "[GSOC] cat-file: add has_object_file() check", "[GSOC] ref-filter: modify the error message and value in get_object", "[GSOC] ref-filter: add cat_file_mode in struct ref_format".
2. Change batch_objects parameter name from "opt" to "batch".
3. Modify test subject.
4. Begin to use the explicit initialization of REF_FORMAT_INIT in the design of %(rest).
5. Fix some grammatical errors and code style issues.

ZheNing Hu (14):
[GSOC] ref-filter: add obj-type check in grab contents [GSOC] ref-filter: add %(raw) atom
[GSOC] ref-filter: --format=%(raw) re-support --perl [GSOC] ref-filter: use non-const ref_format in *_atom_parser() [GSOC] ref-filter: add %(rest) atom
[GSOC] ref-filter: pass get_object() return value to their callers [GSOC] ref-filter: introduce free_ref_array_item_value() function [GSOC] ref-filter: add cat_file_mode in struct ref_format [GSOC] ref-filter: modify the error message and value in get_object [GSOC] cat-file: add has_object_file() check
[GSOC] cat-file: change batch_objects parameter name [GSOC] cat-file: reuse ref-filter logic
[GSOC] cat-file: reuse err buf in batch_object_write() [GSOC] cat-file: re-implement --textconv, --filters options

 Documentation/git-cat-file.txt     |   6 +
 Documentation/git-for-each-ref.txt |   9 +
 builtin/cat-file.c                 | 277 +++++++-----------------
 builtin/tag.c                      |   2 +-
 quote.c                            |  17 ++
 quote.h                            |   1 +
 ref-filter.c                       | 331 +++++++++++++++++++++++------
 ref-filter.h                       |  14 +-
 t/t1006-cat-file.sh                | 252 ++++++++++++++++++++++
 t/t3203-branch-output.sh           |   4 +
 t/t6300-for-each-ref.sh            | 235 ++++++++++++++++++++
 t/t6301-for-each-ref-errors.sh     |   2 +-
 t/t7004-tag.sh                     |   4 +
 t/t7030-verify-tag.sh              |   4 +
 14 files changed, 879 insertions(+), 279 deletions(-)

base-commit: 1197f1a46360d3ae96bd9c15908a3a6f8e562207
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-980%2Fadlternative%2Fcat-file-batch-refactor-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-980/adlternative/cat-file-batch-refactor-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/980

Range-diff vs v3:

  1:  f72ad9cc5e8b =  1:  f72ad9cc5e8b [GSOC] ref-filter: add obj-type check in grab contents
  2:  ab497d66c116 =  2:  ab497d66c116 [GSOC] ref-filter: add %(raw) atom
  3:  b54dbc431e04 =  3:  b54dbc431e04 [GSOC] ref-filter: --format=%(raw) re-support --perl
  4:  9fbbb3c492f5 =  4:  9fbbb3c492f5 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()
  5:  39a0d93c7bc1 !  5:  08aa44e5e57b [GSOC] ref-filter: add %(rest) atom
     @@ ref-filter.h: struct ref_format {
       };
       
      -#define REF_FORMAT_INIT { NULL, 0, -1 }
     -+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
     ++#define REF_FORMAT_INIT { .use_color = -1 }
       
       /*  Macros for checking --merged and --no-merged options */
       #define _OPT_MERGED_NO_MERGED(option, filter, h) \
  6:  35a376db1fc1 =  6:  05682bccf9f9 [GSOC] ref-filter: pass get_object() return value to their callers
  7:  8c1d683ec6e9 =  7:  06db6cd6f1f9 [GSOC] ref-filter: introduce free_ref_array_item_value() function
  -:  ------------ >  8:  b0d9e139935f [GSOC] ref-filter: add cat_file_mode in struct ref_format
  -:  ------------ >  9:  db7dd8b042c2 [GSOC] ref-filter: modify the error message and value in get_object
  -:  ------------ > 10:  6b577969734e [GSOC] cat-file: add has_object_file() check
  -:  ------------ > 11:  069aa203666a [GSOC] cat-file: change batch_objects parameter name
  8:  bd534a266a40 ! 12:  258ec0a46c56 [GSOC] cat-file: reuse ref-filter logic
     @@ Metadata
       ## Commit message ##
          [GSOC] cat-file: reuse ref-filter logic
      
     -    In order to let cat-file use ref-filter logic, the following
     -    methods are used:
     +    In order to let cat-file use ref-filter logic, let's do the
     +    following:
      
     -    1. Add `cat_file_mode` member in struct `ref_format`, this can
     -    help us reject atoms in verify_ref_format() which cat-file
     -    cannot use, e.g. `%(refname)`, `%(push)`, `%(upstream)`...
     -    2. Change the type of member `format` in struct `batch_options`
     -    to `ref_format`, We can add format data in it.
     -    3. Let `batch_objects()` add atoms to format, and use
     +    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.
     -    4. Use `has_object_file()` in `batch_one_object()` to check
     -    whether the input object exists.
     -    5. Let get_object() return 1 and print "<oid> missing" instead
     -    of returning -1 and printing "missing object <oid> for <refname>",
     -    this can help `format_ref_array_item()` just report that the
     -    object is missing without letting Git exit.
     -    6. Use `format_ref_array_item()` in `batch_object_write()` to
     +    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 less than zero, use `die()` to print the error message
     -    and exit; else return value greater than zero, only print the
     -    error message, but not exit.
     -    7. Use free_ref_array_item_value() to free ref_array_item's
     +    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 refname.
     +    `%(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 <[email protected]>
          Mentored-by: Hariom Verma <[email protected]>
     @@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi
      -		print_object_or_die(opt, data);
      -		batch_write(opt, "\n", 1);
      +	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
     -+	if (!ret) {
     -+		strbuf_addch(scratch, '\n');
     -+		batch_write(opt, scratch->buf, scratch->len);
     -+	} else if (ret < 0) {
     ++	if (ret < 0) {
      +		die("%s\n", err.buf);
     -+	} else {
     -+		/* when ret > 0 , don't call die and print the err to stdout*/
     ++	} 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,
     -@@ builtin/cat-file.c: static void batch_one_object(const char *obj_name,
     - 		return;
     - 	}
     - 
     -+	if (!has_object_file(&data->oid)) {
     -+		printf("%s missing\n",
     -+		       obj_name ? obj_name : oid_to_hex(&data->oid));
     -+		fflush(stdout);
     -+		return;
     -+	}
     -+
     - 	batch_object_write(obj_name, scratch, opt, data);
     - }
     - 
      @@ builtin/cat-file.c: static int batch_unordered_packed(const struct object_id *oid,
       	return batch_unordered_object(oid, data);
       }
       
     --static int batch_objects(struct batch_options *opt)
     +-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 *opt, const struct option *options)
     ++static int batch_objects(struct batch_options *batch, const struct option *options)
       {
       	struct strbuf input = STRBUF_INIT;
       	struct strbuf output = STRBUF_INIT;
     @@ builtin/cat-file.c: static int batch_unordered_packed(const struct object_id *oi
       	int save_warning;
       	int retval = 0;
       
     --	if (!opt->format)
     --		opt->format = "%(objectname) %(objecttype) %(objectsize)";
     +-	if (!batch->format)
     +-		batch->format = "%(objectname) %(objecttype) %(objectsize)";
      -
      -	/*
      -	 * Expand once with our special mark_query flag, which will prime the
     @@ builtin/cat-file.c: static int batch_unordered_packed(const struct object_id *oi
      -	 */
       	memset(&data, 0, sizeof(data));
      -	data.mark_query = 1;
     --	strbuf_expand(&output, opt->format, expand_format, &data);
     +-	strbuf_expand(&output, batch->format, expand_format, &data);
      -	data.mark_query = 0;
      -	strbuf_release(&output);
     --	if (opt->cmdmode)
     +-	if (batch->cmdmode)
      -		data.split_on_whitespace = 1;
      -
     --	if (opt->all_objects) {
     +-	if (batch->all_objects) {
      -		struct object_info empty = OBJECT_INFO_INIT;
      -		if (!memcmp(&data.info, &empty, sizeof(empty)))
      -			data.skip_object_info = 1;
     @@ builtin/cat-file.c: static int batch_unordered_packed(const struct object_id *oi
      -	 * 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 (!opt->format.format)
     -+		strbuf_addstr(&format, "%(objectname) %(objecttype) %(objectsize)");
     ++	if (batch->format.format)
     ++		strbuf_addstr(&format, batch->format.format);
      +	else
     -+		strbuf_addstr(&format, opt->format.format);
     - 	if (opt->print_contents)
     ++		strbuf_addstr(&format, "%(objectname) %(objecttype) %(objectsize)");
     + 	if (batch->print_contents)
      -		data.info.typep = &data.type;
      +		strbuf_addstr(&format, "\n%(raw)");
     -+	opt->format.format = format.buf;
     -+	if (verify_ref_format(&opt->format))
     ++	batch->format.format = format.buf;
     ++	if (verify_ref_format(&batch->format))
      +		usage_with_options(cat_file_usage, options);
      +
     -+	if (opt->cmdmode || opt->format.use_rest)
     ++	if (batch->cmdmode || batch->format.use_rest)
      +		data.split_on_whitespace = 1;
       
     - 	if (opt->all_objects) {
     + 	if (batch->all_objects) {
       		struct object_cb_data cb;
     -@@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt)
     +@@ builtin/cat-file.c: static int batch_objects(struct batch_options *batch)
       			oid_array_clear(&sa);
       		}
       
     @@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt)
       		strbuf_release(&output);
       		return 0;
       	}
     -@@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt)
     - 		batch_one_object(input.buf, &output, opt, &data);
     +@@ builtin/cat-file.c: static int batch_objects(struct batch_options *batch)
     + 		batch_one_object(input.buf, &output, batch, &data);
       	}
       
      +	strbuf_release(&format);
     @@ builtin/cat-file.c: int cmd_cat_file(int argc, const char **argv, const char *pr
       	if (unknown_type && opt != 't' && opt != 's')
       		die("git cat-file --allow-unknown-type: use with -s or -t");
      
     - ## ref-filter.c ##
     -@@ ref-filter.c: int verify_ref_format(struct ref_format *format)
     - 		if (at < 0)
     - 			die("%s", err.buf);
     - 
     --		if (used_atom[at].atom_type == ATOM_REST)
     --			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
     -+		if ((!format->cat_file_mode && used_atom[at].atom_type == ATOM_REST) ||
     -+		    (format->cat_file_mode && (used_atom[at].atom_type == ATOM_FLAG ||
     -+					       used_atom[at].atom_type == ATOM_HEAD ||
     -+					       used_atom[at].atom_type == ATOM_PUSH ||
     -+					       used_atom[at].atom_type == ATOM_REFNAME ||
     -+					       used_atom[at].atom_type == ATOM_SYMREF ||
     -+					       used_atom[at].atom_type == ATOM_UPSTREAM ||
     -+					       used_atom[at].atom_type == ATOM_WORKTREEPATH)))
     -+			die(_("this command reject atom %%(%.*s)"), (int)(ep - sp - 2), sp + 2);
     - 
     - 		if ((format->quote_style == QUOTE_PYTHON ||
     - 		     format->quote_style == QUOTE_SHELL ||
     -@@ ref-filter.c: static int get_object(struct ref_array_item *ref, int deref, struct object **obj
     - 	}
     - 	if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
     - 				     OBJECT_INFO_LOOKUP_REPLACE))
     --		return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
     --				       oid_to_hex(&oi->oid), ref->refname);
     -+		return strbuf_addf_ret(err, 1, _("%s missing"),
     -+				       oid_to_hex(&oi->oid));
     - 	if (oi->info.disk_sizep && oi->disk_size < 0)
     - 		BUG("Object size is less than zero.");
     - 
     -
     - ## ref-filter.h ##
     -@@ ref-filter.h: struct ref_format {
     - 	 */
     - 	const char *format;
     - 	const char *rest;
     -+	int cat_file_mode;
     - 	int quote_style;
     - 	int use_rest;
     - 	int use_color;
     -@@ ref-filter.h: struct ref_format {
     - 	int need_color_reset_at_eol;
     - };
     - 
     --#define REF_FORMAT_INIT { NULL, NULL, 0, 0, -1 }
     -+#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
     - 
     - /*  Macros for checking --merged and --no-merged options */
     - #define _OPT_MERGED_NO_MERGED(option, filter, h) \
     -
       ## t/t1006-cat-file.sh ##
      @@ t/t1006-cat-file.sh: test_expect_success 'cat-file --unordered works' '
       	test_cmp expect actual
     @@ t/t1006-cat-file.sh: test_expect_success 'cat-file --unordered works' '
      +batch_test_atom refs/heads/main '%(INVALID)' fail
      +batch_test_atom refs/heads/main '%(authordate:INVALID)' fail
      +
     -+test_expect_success 'cat-file refs/heads/main refs/tags/testtag %(rest)' '
     ++test_expect_success '%(rest) works with both a branch and a tag' '
      +	cat >expected <<-EOF &&
      +	123 commit 123
      +	456 tag 456
     @@ t/t1006-cat-file.sh: test_expect_success 'cat-file --unordered works' '
      +'
      +
       test_done
     -
     - ## t/t6301-for-each-ref-errors.sh ##
     -@@ t/t6301-for-each-ref-errors.sh: test_expect_success 'Missing objects are reported correctly' '
     - 	r=refs/heads/missing &&
     - 	echo $MISSING >.git/$r &&
     - 	test_when_finished "rm -f .git/$r" &&
     --	echo "fatal: missing object $MISSING for $r" >missing-err &&
     -+	echo "fatal: $MISSING missing" >missing-err &&
     - 	test_must_fail git for-each-ref 2>err &&
     - 	test_cmp missing-err err &&
     - 	(
  9:  b66ab0f2d569 ! 13:  bda6aae9a6c9 [GSOC] cat-file: reuse err buf in batch_object_write()
     @@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi
       
      -	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
      +	ret = format_ref_array_item(&item, &opt->format, scratch, err);
     - 	if (!ret) {
     - 		strbuf_addch(scratch, '\n');
     - 		batch_write(opt, scratch->buf, scratch->len);
     - 	} else if (ret < 0) {
     + 	if (ret < 0) {
      -		die("%s\n", err.buf);
      +		die("%s\n", err->buf);
     - 	} else {
     - 		/* when ret > 0 , don't call die and print the err to stdout*/
     + 	} 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);
      +		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);
     @@ builtin/cat-file.c: struct object_cb_data {
       	return 0;
       }
       
     -@@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt, const struct option *options
     +@@ builtin/cat-file.c: static int batch_objects(struct batch_options *batch, const struct option *optio
       {
       	struct strbuf input = STRBUF_INIT;
       	struct strbuf output = STRBUF_INIT;
     @@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt, const st
       	struct strbuf format = STRBUF_INIT;
       	struct expand_data data;
       	int save_warning;
     -@@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt, const struct option *options
     - 		cb.opt = opt;
     +@@ builtin/cat-file.c: static int batch_objects(struct batch_options *batch, const struct option *optio
     + 		cb.opt = batch;
       		cb.expand = &data;
       		cb.scratch = &output;
      +		cb.err = &err;
       
     - 		if (opt->unordered) {
     + 		if (batch->unordered) {
       			struct oidset seen = OIDSET_INIT;
     -@@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt, const struct option *options
     +@@ builtin/cat-file.c: static int batch_objects(struct batch_options *batch, const struct option *optio
       
       		strbuf_release(&format);
       		strbuf_release(&output);
     @@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt, const st
       		return 0;
       	}
       
     -@@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt, const struct option *options
     +@@ builtin/cat-file.c: static int batch_objects(struct batch_options *batch, const struct option *optio
       			data.rest = p;
       		}
       
     --		batch_one_object(input.buf, &output, opt, &data);
     -+		batch_one_object(input.buf, &output, &err, opt, &data);
     +-		batch_one_object(input.buf, &output, batch, &data);
     ++		batch_one_object(input.buf, &output, &err, batch, &data);
       	}
       
       	strbuf_release(&format);
 10:  86ac3bcaecea ! 14:  d1114a2bd743 [GSOC] cat-file: re-implement --textconv, --filters options
     @@ Commit message
          Signed-off-by: ZheNing Hu <[email protected]>
      
       ## builtin/cat-file.c ##
     -@@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt, const struct option *options
     - 	if (opt->print_contents)
     +@@ builtin/cat-file.c: static int batch_objects(struct batch_options *batch, const struct option *optio
     + 	if (batch->print_contents)
       		strbuf_addstr(&format, "\n%(raw)");
     - 	opt->format.format = format.buf;
     -+	if (opt->cmdmode == 'c')
     -+		opt->format.use_textconv = 1;
     -+	else if (opt->cmdmode == 'w')
     -+		opt->format.use_filters = 1;
     + 	batch->format.format = format.buf;
      +
     - 	if (verify_ref_format(&opt->format))
     ++	if (batch->cmdmode == 'c')
     ++		batch->format.use_textconv = 1;
     ++	else if (batch->cmdmode == 'w')
     ++		batch->format.use_filters = 1;
     ++
     + 	if (verify_ref_format(&batch->format))
       		usage_with_options(cat_file_usage, options);
       
      
     @@ ref-filter.h: struct ref_format {
       	int use_rest;
       	int use_color;
       
     -@@ ref-filter.h: struct ref_format {
     - 	int need_color_reset_at_eol;
     - };
     - 
     --#define REF_FORMAT_INIT { NULL, NULL, 0, 0, 0, -1 }
     -+#define REF_FORMAT_INIT { .use_color = -1 }
     - 
     - /*  Macros for checking --merged and --no-merged options */
     - #define _OPT_MERGED_NO_MERGED(option, filter, h) \
Signature · 1 line
gitgitgadget
#58 lore raw

PATCH v4 1/14 ref-filter: add obj-type check in grab contents

From: ZheNing Hu <[email protected]>

Only tag and commit objects use grab_sub_body_contents() to grab object contents in the current codebase. We want to teach the function to also handle blobs and trees to get their raw data, without parsing a blob (whose contents looks like a commit or a tag) incorrectly as a commit or a tag.

Skip the block of code that is specific to handling commits and tags early when the given object is of a wrong type to help later addition to handle other types of objects in this function.

---
 ref-filter.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)
ref-filter.c +15 -9
index 4db0e40ff4c6..5cee6512fbaf 100644
@@ -1356,11 +1356,12 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
 }
 
 /* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
 {
 	int i;
 	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
 	size_t sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+	void *buf = data->content;
 
 	for (i = 0; i < used_atom_cnt; i++) {
 		struct used_atom *atom = &used_atom[i];
@@ -1371,10 +1372,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
 			continue;
 		if (deref)
 			name++;
-		if (strcmp(name, "body") &&
-		    !starts_with(name, "subject") &&
-		    !starts_with(name, "trailers") &&
-		    !starts_with(name, "contents"))
+
+		if ((data->type != OBJ_TAG &&
+		     data->type != OBJ_COMMIT) ||
+		    (strcmp(name, "body") &&
+		     !starts_with(name, "subject") &&
+		     !starts_with(name, "trailers") &&
+		     !starts_with(name, "contents")))
 			continue;
 		if (!subpos)
 			find_subpos(buf,
@@ -1438,17 +1442,19 @@ static void fill_missing_values(struct atom_value *val)
  * pointed at by the ref itself; otherwise it is the object the
  * ref (which is a tag) refers to.
  */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
+static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
 {
+	void *buf = data->content;
+
 	switch (obj->type) {
 	case OBJ_TAG:
 		grab_tag_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("tagger", val, deref, buf);
 		break;
 	case OBJ_COMMIT:
 		grab_commit_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("author", val, deref, buf);
 		grab_person("committer", val, deref, buf);
 		break;
@@ -1678,7 +1684,7 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 			return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
 					       oid_to_hex(&oi->oid), ref->refname);
 		}
-		grab_values(ref->value, deref, *obj, oi->content);
+		grab_values(ref->value, deref, *obj, oi);
 	}
 
 	grab_common_values(ref->value, deref, oi);
Signature · 1 line
gitgitgadget
#59 lore raw

PATCH v4 2/14 ref-filter: add %(raw) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add new formatting option %(raw), which will print the raw object data without any changes. It will help further to migrate all cat-file formatting logic from cat-file to ref-filter.

The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).

E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.

Therefore, we need to find a way to record the length of the atom_value's member s. Although strbuf can already record the string and its length, if we want to replace the type of atom_value's member s with strbuf, many places in ref-filter that are filled with dynamically allocated mermory in v->s are not easy to replace. At the same time, we need to check if v->s == NULL in populate_value(), and strbuf cannot easily distinguish NULL and empty strings, but c-style "const char *" can do it. So add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

---
 Documentation/git-for-each-ref.txt |   9 ++
 ref-filter.c                       | 139 +++++++++++++++----
 t/t6300-for-each-ref.sh            | 216 +++++++++++++++++++++++++++++
 3 files changed, 337 insertions(+), 27 deletions(-)
Documentation/git-for-each-ref.txt +9 -0
index 2ae2478de706..7f1f0a1ca3b6 100644
@@ -235,6 +235,15 @@ and `date` to extract the named component.  For email fields (`authoremail`,
 without angle brackets, and `:localpart` to get the part before the `@` symbol
 out of the trimmed email.
 
+The raw data in an object is `raw`.
+
+raw:size::
+	The raw data size of the object.
+
+Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
+`--perl` because the host language may not support arbitrary binary data in the
+variables of its string type.
+
 The message in a commit or a tag object is `contents`, from which
 `contents:<part>` can be used to extract various parts out of:
 
ref-filter.c +112 -27

293 lines not on this page · raw message

t/t6300-for-each-ref.sh +216 -0

258 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#60 lore raw

Re: [PATCH v4 02/14] [GSOC] ref-filter: add %(raw) atom

Quoted 4 lines · ZheNing Hu via GitGitGadget

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

Better say:

"Note that --format=%(raw) cannot be used with --python, --shell, --tcl, and --perl because if the binary raw data is passed to a variable in such languages, these may not support arbitrary binary data in their string variable type."

Signature · 1 line
An old man doll... just what I always wanted! - Clara
#61 lore raw

Re: [PATCH v4 02/14] [GSOC] ref-filter: add %(raw) atom

Quoted 7 lines · Bagas Sanjaya <[email protected]> 于2021年6月24日周四 下午12:14写道:
Quoted 4 lines · ZheNing Hu via GitGitGadget

Beyond, --format=%(raw) cannot be used with --python, --shell, --tcl, --perl because if our binary raw data is passed to a variable in the host language, the host language may not support arbitrary binary data in the variables of its string type.

Quoted 7 lines

Better say:

"Note that --format=%(raw) cannot be used with --python, --shell, --tcl, and --perl because if the binary raw data is passed to a variable in such languages, these may not support arbitrary binary data in their string variable type."

Thanks, Bagas Sanjaya, I will change all of them.

Quoted 2 lines

--
An old man doll... just what I always wanted! - Clara

--
ZheNing Hu

#62 lore raw

PATCH v4 3/14 ref-filter: --format=%(raw) re-support --perl

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because the perl language can handle binary data correctly, add the function perl_quote_buf_with_len(), which can specify the length of the data and prevent the data from being truncated at '\0' to help --format="%(raw)" re-support --perl.

---
 Documentation/git-for-each-ref.txt |  2 +-
 quote.c                            | 17 +++++++++++++++++
 quote.h                            |  1 +
 ref-filter.c                       | 15 +++++++++++----
 t/t6300-for-each-ref.sh            | 19 +++++++++++++++++--
 5 files changed, 47 insertions(+), 7 deletions(-)
Documentation/git-for-each-ref.txt +1 -1
index 7f1f0a1ca3b6..ea9b438c16f6 100644
@@ -241,7 +241,7 @@ raw:size::
 	The raw data size of the object.
 
 Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
-`--perl` because the host language may not support arbitrary binary data in the
+because the host language may not support arbitrary binary data in the
 variables of its string type.
 
 The message in a commit or a tag object is `contents`, from which
quote.c +17 -0
index 8a3a5e39eb12..26719d21d1e7 100644
@@ -471,6 +471,23 @@ void perl_quote_buf(struct strbuf *sb, const char *src)
 	strbuf_addch(sb, sq);
 }
 
+void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len)
+{
+	const char sq = '\'';
+	const char bq = '\\';
+	const char *c = src;
+	const char *end = src + len;
+
+	strbuf_addch(sb, sq);
+	while (c != end) {
+		if (*c == sq || *c == bq)
+			strbuf_addch(sb, bq);
+		strbuf_addch(sb, *c);
+		c++;
+	}
+	strbuf_addch(sb, sq);
+}
+
 void python_quote_buf(struct strbuf *sb, const char *src)
 {
 	const char sq = '\'';
quote.h +1 -0
index 768cc6338e27..0fe69e264b0e 100644
@@ -94,6 +94,7 @@ char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigne
 
 /* quoting as a string literal for other languages */
 void perl_quote_buf(struct strbuf *sb, const char *src);
+void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len);
 void python_quote_buf(struct strbuf *sb, const char *src);
 void tcl_quote_buf(struct strbuf *sb, const char *src);
 void basic_regex_quote_buf(struct strbuf *sb, const char *src);
ref-filter.c +11 -4
index 7822be903071..797b20ffa612 100644
@@ -742,7 +742,10 @@ static void quote_formatting(struct strbuf *s, const char *str, size_t len, int
 		sq_quote_buf(s, str);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(s, str);
+		if (len != ATOM_VALUE_S_SIZE_INIT)
+			perl_quote_buf_with_len(s, str, len);
+		else
+			perl_quote_buf(s, str);
 		break;
 	case QUOTE_PYTHON:
 		python_quote_buf(s, str);
@@ -1006,10 +1009,14 @@ int verify_ref_format(struct ref_format *format)
 		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
 		if (at < 0)
 			die("%s", err.buf);
-		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
-		    used_atom[at].u.raw_data.option == RAW_BARE)
+
+		if ((format->quote_style == QUOTE_PYTHON ||
+		     format->quote_style == QUOTE_SHELL ||
+		     format->quote_style == QUOTE_TCL) &&
+		     used_atom[at].atom_type == ATOM_RAW &&
+		     used_atom[at].u.raw_data.option == RAW_BARE)
 			die(_("--format=%.*s cannot be used with"
-			      "--python, --shell, --tcl, --perl"), (int)(ep - sp - 2), sp + 2);
+			      "--python, --shell, --tcl"), (int)(ep - sp - 2), sp + 2);
 		cp = ep + 1;
 
 		if (skip_prefix(used_atom[at].name, "color:", &color))
t/t6300-for-each-ref.sh +17 -2
index 9c5379e2f56f..5556063c347d 100755
@@ -915,8 +915,23 @@ test_expect_success '%(raw) with --tcl must fail' '
 	test_must_fail git for-each-ref --format="%(raw)" --tcl
 '
 
-test_expect_success '%(raw) with --perl must fail' '
-	test_must_fail git for-each-ref --format="%(raw)" --perl
+test_expect_success '%(raw) with --perl' '
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob1 --perl | perl > actual &&
+	cmp blob1 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob3 --perl | perl > actual &&
+	cmp blob3 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob8 --perl | perl > actual &&
+	cmp blob8 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/first --perl | perl > actual &&
+	cmp one actual &&
+	git cat-file tree refs/mytrees/first > expected &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/mytrees/first --perl | perl > actual &&
+	cmp expected actual
 '
 
 test_expect_success '%(raw) with --shell must fail' '
Signature · 1 line
gitgitgadget
#63 lore raw

PATCH v4 4/14 ref-filter: use non-const ref_format in *_atom_parser()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Use non-const ref_format in *_atom_parser(), which can help us modify the members of ref_format in *_atom_parser().

---
 builtin/tag.c |  2 +-
 ref-filter.c  | 44 ++++++++++++++++++++++----------------------
 ref-filter.h  |  4 ++--
 3 files changed, 25 insertions(+), 25 deletions(-)
builtin/tag.c +1 -1
index 82fcfc098242..452558ec9575 100644
@@ -146,7 +146,7 @@ static int verify_tag(const char *name, const char *ref,
 		      const struct object_id *oid, void *cb_data)
 {
 	int flags;
-	const struct ref_format *format = cb_data;
+	struct ref_format *format = cb_data;
 	flags = GPG_VERIFY_VERBOSE;
 
 	if (format->format)
ref-filter.c +22 -22

164 lines not on this page · raw message

ref-filter.h +2 -2
index baf72a718965..74fb423fc89f 100644
@@ -116,7 +116,7 @@ void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
 void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, unsigned int mask, int on);
 /*  Based on the given format and quote_style, fill the strbuf */
 int format_ref_array_item(struct ref_array_item *info,
-			  const struct ref_format *format,
+			  struct ref_format *format,
 			  struct strbuf *final_buf,
 			  struct strbuf *error_buf);
 /*  Parse a single sort specifier and add it to the list */
@@ -137,7 +137,7 @@ void setup_ref_filter_porcelain_msg(void);
  * name must be a fully qualified refname.
  */
 void pretty_print_ref(const char *name, const struct object_id *oid,
-		      const struct ref_format *format);
+		      struct ref_format *format);
 
 /*
  * Push a single ref onto the array; this can be used to construct your own
Signature · 1 line
gitgitgadget
#64 lore raw

PATCH v4 5/14 ref-filter: add %(rest) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let "cat-file --batch=%(rest)" use the ref-filter interface, add %(rest) atom for ref-filter. "git for-each-ref", "git branch", "git tag" and "git verify-tag" will reject %(rest) by default.

---
 ref-filter.c             | 21 +++++++++++++++++++++
 ref-filter.h             |  5 ++++-
 t/t3203-branch-output.sh |  4 ++++
 t/t6300-for-each-ref.sh  |  4 ++++
 t/t7004-tag.sh           |  4 ++++
 t/t7030-verify-tag.sh    |  4 ++++
 6 files changed, 41 insertions(+), 1 deletion(-)
ref-filter.c +21 -0
index d01a0266fb89..10c78de9cfa4 100644
@@ -157,6 +157,7 @@ enum atom_type {
 	ATOM_IF,
 	ATOM_THEN,
 	ATOM_ELSE,
+	ATOM_REST,
 };
 
 /*
@@ -559,6 +560,15 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
 	return 0;
 }
 
+static int rest_atom_parser(struct ref_format *format, struct used_atom *atom,
+			    const char *arg, struct strbuf *err)
+{
+	if (arg)
+		return strbuf_addf_ret(err, -1, _("%%(rest) does not take arguments"));
+	format->use_rest = 1;
+	return 0;
+}
+
 static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
 			    const char *arg, struct strbuf *unused_err)
 {
@@ -615,6 +625,7 @@ static struct {
 	[ATOM_IF] = { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
 	[ATOM_THEN] = { "then", SOURCE_NONE },
 	[ATOM_ELSE] = { "else", SOURCE_NONE },
+	[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
 	/*
 	 * Please update $__git_ref_fieldlist in git-completion.bash
 	 * when you add new atoms
@@ -1010,6 +1021,9 @@ int verify_ref_format(struct ref_format *format)
 		if (at < 0)
 			die("%s", err.buf);
 
+		if (used_atom[at].atom_type == ATOM_REST)
+			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+
 		if ((format->quote_style == QUOTE_PYTHON ||
 		     format->quote_style == QUOTE_SHELL ||
 		     format->quote_style == QUOTE_TCL) &&
@@ -1927,6 +1941,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 			v->handler = else_atom_handler;
 			v->s = xstrdup("");
 			continue;
+		} else if (atom_type == ATOM_REST) {
+			if (ref->rest)
+				v->s = xstrdup(ref->rest);
+			else
+				v->s = xstrdup("");
+			continue;
 		} else
 			continue;
 
@@ -2144,6 +2164,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
 
 	FLEX_ALLOC_STR(ref, refname, refname);
 	oidcpy(&ref->objectname, oid);
+	ref->rest = NULL;
 
 	return ref;
 }
ref-filter.h +4 -1
index 74fb423fc89f..c15dee8d6b95 100644
@@ -38,6 +38,7 @@ struct ref_sorting {
 
 struct ref_array_item {
 	struct object_id objectname;
+	const char *rest;
 	int flag;
 	unsigned int kind;
 	const char *symref;
@@ -76,14 +77,16 @@ struct ref_format {
 	 * verify_ref_format() afterwards to finalize.
 	 */
 	const char *format;
+	const char *rest;
 	int quote_style;
+	int use_rest;
 	int use_color;
 
 	/* Internal state to ref-filter */
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, 0, -1 }
+#define REF_FORMAT_INIT { .use_color = -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
t/t3203-branch-output.sh +4 -0
index 5325b9f67a00..6e94c6db7b5a 100755
@@ -340,6 +340,10 @@ test_expect_success 'git branch --format option' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch with --format=%(rest) must fail' '
+	test_must_fail git branch --format="%(rest)" >actual
+'
+
 test_expect_success 'worktree colors correct' '
 	cat >expect <<-EOF &&
 	* <GREEN>(HEAD detached from fromtag)<RESET>
t/t6300-for-each-ref.sh +4 -0
index 5556063c347d..82c0ad2cb115 100755
@@ -1211,6 +1211,10 @@ test_expect_success 'basic atom: head contents:trailers' '
 	test_cmp expect actual.clean
 '
 
+test_expect_success 'basic atom: rest must fail' '
+	test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
+'
+
 test_expect_success 'trailer parsing not fooled by --- line' '
 	git commit --allow-empty -F - <<-\EOF &&
 	this is the subject
t/t7004-tag.sh +4 -0
index 2f72c5c6883e..082be85dffc7 100755
@@ -1998,6 +1998,10 @@ test_expect_success '--format should list tags as per format given' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git tag -l with --format="%(rest)" must fail' '
+	test_must_fail git tag -l --format="%(rest)" "v1*"
+'
+
 test_expect_success "set up color tests" '
 	echo "<RED>v1.0<RESET>" >expect.color &&
 	echo "v1.0" >expect.bare &&
t/t7030-verify-tag.sh +4 -0
index 3cefde9602bf..10faa645157e 100755
@@ -194,6 +194,10 @@ test_expect_success GPG 'verifying tag with --format' '
 	test_cmp expect actual
 '
 
+test_expect_success GPG 'verifying tag with --format="%(rest)" must fail' '
+	test_must_fail git verify-tag --format="%(rest)" "fourth-signed"
+'
+
 test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
 	test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
 	test_must_be_empty actual-forged
Signature · 1 line
gitgitgadget
#65 lore raw

PATCH v4 6/14 ref-filter: pass get_object() return value to their callers

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Since in the refactor of git cat-file --batch later, oid_object_info_extended() in get_object() will be used to obtain the info of an object with it's oid. When the object cannot be obtained in the git repository, cat-file --batch expects to output "<oid> missing" and continue the next oid query instead of letting Git exit. In other error conditions, Git should exit normally. So we can achieve this function by passing the return value of get_object().

---
 ref-filter.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)
ref-filter.c +11 -6
index 10c78de9cfa4..58def6ccd33a 100644
@@ -1816,6 +1816,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
 	struct object *obj;
 	int i;
+	int ret = 0;
 	struct object_info empty = OBJECT_INFO_INIT;
 
 	CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -1972,8 +1973,9 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
 
 	oi.oid = ref->objectname;
-	if (get_object(ref, 0, &obj, &oi, err))
-		return -1;
+	ret = get_object(ref, 0, &obj, &oi, err);
+	if (ret)
+		return ret;
 
 	/*
 	 * If there is no atom that wants to know about tagged
@@ -2005,8 +2007,10 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom,
 			      struct atom_value **v, struct strbuf *err)
 {
 	if (!ref->value) {
-		if (populate_value(ref, err))
-			return -1;
+		int ret = populate_value(ref, err);
+
+		if (ret)
+			return ret;
 		fill_missing_values(ref->value);
 	}
 	*v = &ref->value[atom];
@@ -2580,6 +2584,7 @@ int format_ref_array_item(struct ref_array_item *info,
 {
 	const char *cp, *sp, *ep;
 	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+	int ret = 0;
 
 	state.quote_style = format->quote_style;
 	push_stack_element(&state.stack);
@@ -2592,10 +2597,10 @@ int format_ref_array_item(struct ref_array_item *info,
 		if (cp < sp)
 			append_literal(cp, sp, &state);
 		pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-		if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+		if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||
 		    atomv->handler(atomv, &state, error_buf)) {
 			pop_stack_element(&state.stack);
-			return -1;
+			return ret ? ret : -1;
 		}
 	}
 	if (*cp) {
Signature · 1 line
gitgitgadget
#66 lore raw

Re: [PATCH v4 06/14] [GSOC] ref-filter: pass get_object() return value to their callers

Quoted 10 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Since in the refactor of git cat-file --batch later, oid_object_info_extended() in get_object() will be used to obtain the info of an object with it's oid. When the object cannot be obtained in the git repository, cat-file --batch expects to output "<oid> missing" and continue the next oid query instead of letting Git exit. In other error conditions, Git should exit normally. So we can achieve this function by passing the return value of get_object().

s/Since/Because/

Signature · 1 line
An old man doll... just what I always wanted! - Clara
#67 lore raw

PATCH v4 7/14 ref-filter: introduce free_ref_array_item_value() function

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

When we use ref_array_item which is not dynamically allocated and want to free the space of its member "value" after the end of use, free_array_item() does not meet our needs, because it tries to free ref_array_item itself and its member "symref".

Introduce free_ref_array_item_value() for freeing ref_array_item value. It will be called internally by free_array_item(), and it will help cat-file --batch free ref_array_item's value memory later.

---
 ref-filter.c | 11 ++++++++---
 ref-filter.h |  2 ++
 2 files changed, 10 insertions(+), 3 deletions(-)
ref-filter.c +8 -3
index 58def6ccd33a..22315d4809dc 100644
@@ -2291,16 +2291,21 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 	return 0;
 }
 
-/*  Free memory allocated for a ref_array_item */
-static void free_array_item(struct ref_array_item *item)
+void free_ref_array_item_value(struct ref_array_item *item)
 {
-	free((char *)item->symref);
 	if (item->value) {
 		int i;
 		for (i = 0; i < used_atom_cnt; i++)
 			free((char *)item->value[i].s);
 		free(item->value);
 	}
+}
+
+/*  Free memory allocated for a ref_array_item */
+static void free_array_item(struct ref_array_item *item)
+{
+	free((char *)item->symref);
+	free_ref_array_item_value(item);
 	free(item);
 }
 
ref-filter.h +2 -0
index c15dee8d6b95..44e6dc05ac2f 100644
@@ -111,6 +111,8 @@ struct ref_format {
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
 /*  Clear all memory allocated to ref_array */
 void ref_array_clear(struct ref_array *array);
+/* Free ref_array_item's value */
+void free_ref_array_item_value(struct ref_array_item *item);
 /*  Used to verify if the given format is correct and to parse out the used atoms */
 int verify_ref_format(struct ref_format *format);
 /*  Sort the given ref_array as per the ref_sorting provided */
Signature · 1 line
gitgitgadget
#68 lore raw

PATCH v4 8/14 ref-filter: add cat_file_mode in struct ref_format

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add cat_file_mode member in struct ref_format, when cat-file --batch use ref-filter logic later, it can help us reject atoms in verify_ref_format() which cat-file cannot use, e.g. %(refname), %(push), %(upstream)...

---
 ref-filter.c | 11 +++++++++--
 ref-filter.h |  1 +
 2 files changed, 10 insertions(+), 2 deletions(-)
ref-filter.c +9 -2
index 22315d4809dc..f21f41df0d88 100644
@@ -1021,8 +1021,15 @@ int verify_ref_format(struct ref_format *format)
 		if (at < 0)
 			die("%s", err.buf);
 
-		if (used_atom[at].atom_type == ATOM_REST)
-			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+		if ((!format->cat_file_mode && used_atom[at].atom_type == ATOM_REST) ||
+		    (format->cat_file_mode && (used_atom[at].atom_type == ATOM_FLAG ||
+					       used_atom[at].atom_type == ATOM_HEAD ||
+					       used_atom[at].atom_type == ATOM_PUSH ||
+					       used_atom[at].atom_type == ATOM_REFNAME ||
+					       used_atom[at].atom_type == ATOM_SYMREF ||
+					       used_atom[at].atom_type == ATOM_UPSTREAM ||
+					       used_atom[at].atom_type == ATOM_WORKTREEPATH)))
+			die(_("this command reject atom %%(%.*s)"), (int)(ep - sp - 2), sp + 2);
 
 		if ((format->quote_style == QUOTE_PYTHON ||
 		     format->quote_style == QUOTE_SHELL ||
ref-filter.h +1 -0
index 44e6dc05ac2f..053980a6a426 100644
@@ -78,6 +78,7 @@ struct ref_format {
 	 */
 	const char *format;
 	const char *rest;
+	int cat_file_mode;
 	int quote_style;
 	int use_rest;
 	int use_color;
Signature · 1 line
gitgitgadget
#69 lore raw

PATCH v4 9/14 ref-filter: modify the error message and value in get_object

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Let get_object() return 1 and print "<oid> missing" instead of returning -1 and printing "missing object <oid> for <refname>" if oid_object_info_extended() unable to find the data corresponding to oid. When cat-file --batch use ref-filter logic later it can help format_ref_array_item() just report that the object is missing without letting Git exit.

---
 ref-filter.c                   | 4 ++--
 t/t6301-for-each-ref-errors.sh | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
ref-filter.c +2 -2
index f21f41df0d88..181d99c92735 100644
@@ -1749,8 +1749,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 	}
 	if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
 				     OBJECT_INFO_LOOKUP_REPLACE))
-		return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
-				       oid_to_hex(&oi->oid), ref->refname);
+		return strbuf_addf_ret(err, 1, _("%s missing"),
+				       oid_to_hex(&oi->oid));
 	if (oi->info.disk_sizep && oi->disk_size < 0)
 		BUG("Object size is less than zero.");
 
t/t6301-for-each-ref-errors.sh +1 -1
index 40edf9dab534..3553f84a00c1 100755
@@ -41,7 +41,7 @@ test_expect_success 'Missing objects are reported correctly' '
 	r=refs/heads/missing &&
 	echo $MISSING >.git/$r &&
 	test_when_finished "rm -f .git/$r" &&
-	echo "fatal: missing object $MISSING for $r" >missing-err &&
+	echo "fatal: $MISSING missing" >missing-err &&
 	test_must_fail git for-each-ref 2>err &&
 	test_cmp missing-err err &&
 	(
Signature · 1 line
gitgitgadget
#70 lore raw

PATCH v4 10/14 cat-file: add has_object_file() check

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Use has_object_file() in batch_one_object() to check whether the input object exists. This can help us reject the missing oid when we let cat-file --batch use ref-filter logic later.

---
 builtin/cat-file.c | 7 +++++++
 1 file changed, 7 insertions(+)
builtin/cat-file.c +7 -0
index 5ebf13359e83..9fd3c04ff20b 100644
@@ -428,6 +428,13 @@ static void batch_one_object(const char *obj_name,
 		return;
 	}
 
+	if (!has_object_file(&data->oid)) {
+		printf("%s missing\n",
+		       obj_name ? obj_name : oid_to_hex(&data->oid));
+		fflush(stdout);
+		return;
+	}
+
 	batch_object_write(obj_name, scratch, opt, data);
 }
 
Signature · 1 line
gitgitgadget
#71 lore raw

PATCH v4 11/14 cat-file: change batch_objects parameter name

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Since cat-file reuses ref-filter logic later will add the formal parameter "const struct option *options" to batch_objects(), the two synonymous parameters of "opt" and "options" may confuse readers, so change batch_options parameter of batch_objects() from "opt" to "batch".

---
 builtin/cat-file.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
builtin/cat-file.c +11 -11
index 9fd3c04ff20b..cd84c39df968 100644
@@ -495,7 +495,7 @@ static int batch_unordered_packed(const struct object_id *oid,
 	return batch_unordered_object(oid, data);
 }
 
-static int batch_objects(struct batch_options *opt)
+static int batch_objects(struct batch_options *batch)
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
@@ -503,8 +503,8 @@ static int batch_objects(struct batch_options *opt)
 	int save_warning;
 	int retval = 0;
 
-	if (!opt->format)
-		opt->format = "%(objectname) %(objecttype) %(objectsize)";
+	if (!batch->format)
+		batch->format = "%(objectname) %(objecttype) %(objectsize)";
 
 	/*
 	 * Expand once with our special mark_query flag, which will prime the
@@ -513,13 +513,13 @@ static int batch_objects(struct batch_options *opt)
 	 */
 	memset(&data, 0, sizeof(data));
 	data.mark_query = 1;
-	strbuf_expand(&output, opt->format, expand_format, &data);
+	strbuf_expand(&output, batch->format, expand_format, &data);
 	data.mark_query = 0;
 	strbuf_release(&output);
-	if (opt->cmdmode)
+	if (batch->cmdmode)
 		data.split_on_whitespace = 1;
 
-	if (opt->all_objects) {
+	if (batch->all_objects) {
 		struct object_info empty = OBJECT_INFO_INIT;
 		if (!memcmp(&data.info, &empty, sizeof(empty)))
 			data.skip_object_info = 1;
@@ -529,20 +529,20 @@ static int batch_objects(struct batch_options *opt)
 	 * 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 (opt->print_contents)
+	if (batch->print_contents)
 		data.info.typep = &data.type;
 
-	if (opt->all_objects) {
+	if (batch->all_objects) {
 		struct object_cb_data cb;
 
 		if (has_promisor_remote())
 			warning("This repository uses promisor remotes. Some objects may not be loaded.");
 
-		cb.opt = opt;
+		cb.opt = batch;
 		cb.expand = &data;
 		cb.scratch = &output;
 
-		if (opt->unordered) {
+		if (batch->unordered) {
 			struct oidset seen = OIDSET_INIT;
 
 			cb.seen = &seen;
@@ -592,7 +592,7 @@ static int batch_objects(struct batch_options *opt)
 			data.rest = p;
 		}
 
-		batch_one_object(input.buf, &output, opt, &data);
+		batch_one_object(input.buf, &output, batch, &data);
 	}
 
 	strbuf_release(&input);
Signature · 1 line
gitgitgadget
#72 lore raw

Re: [PATCH v4 11/14] [GSOC] cat-file: change batch_objects parameter name

Quoted 8 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Since cat-file reuses ref-filter logic later will add the formal parameter "const struct option *options" to batch_objects(), the two synonymous parameters of "opt" and "options" may confuse readers, so change batch_options parameter of batch_objects() from "opt" to "batch".

Better say "Because later cat-file reuses ref-filter logic that will add parameter ... ".

Signature · 1 line
An old man doll... just what I always wanted! - Clara
#73 lore raw

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

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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

Quoted 1 line

/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".

---
 Documentation/git-cat-file.txt |   6 +
 builtin/cat-file.c             | 244 ++++++-------------------------
 t/t1006-cat-file.sh            | 252 +++++++++++++++++++++++++++++++++
 3 files changed, 305 insertions(+), 197 deletions(-)
Documentation/git-cat-file.txt +6 -0
index 4eb0421b3fd9..ef8ab952b2fa 100644
@@ -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)`.
 
builtin/cat-file.c +47 -197

339 lines not on this page · raw message

t/t1006-cat-file.sh +252 -0

256 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#74 lore raw

PATCH v4 13/14 cat-file: reuse err buf in batch_object_write()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Reuse the err buffer in batch_object_write(), as the buffer scratch does. This will reduce the overhead of multiple allocations of memory of the err buffer.

---
 builtin/cat-file.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
builtin/cat-file.c +14 -8
index 0e7ad038e5fb..27403326e7a7 100644
@@ -212,35 +212,36 @@ static void batch_write(struct batch_options *opt, const void *data, int len)
 
 static void batch_object_write(const char *obj_name,
 			       struct strbuf *scratch,
+			       struct strbuf *err,
 			       struct batch_options *opt,
 			       struct expand_data *data)
 {
 	int ret = 0;
-	struct strbuf err = STRBUF_INIT;
 	struct ref_array_item item = { data->oid, data->rest };
 
 	strbuf_reset(scratch);
+	strbuf_reset(err);
 
-	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+	ret = format_ref_array_item(&item, &opt->format, scratch, err);
 	if (ret < 0) {
-		die("%s\n", err.buf);
+		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);
+		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,
 			     struct strbuf *scratch,
+			     struct strbuf *err,
 			     struct batch_options *opt,
 			     struct expand_data *data)
 {
@@ -294,7 +295,7 @@ static void batch_one_object(const char *obj_name,
 		return;
 	}
 
-	batch_object_write(obj_name, scratch, opt, data);
+	batch_object_write(obj_name, scratch, err, opt, data);
 }
 
 struct object_cb_data {
@@ -302,13 +303,14 @@ struct object_cb_data {
 	struct expand_data *expand;
 	struct oidset *seen;
 	struct strbuf *scratch;
+	struct strbuf *err;
 };
 
 static int batch_object_cb(const struct object_id *oid, void *vdata)
 {
 	struct object_cb_data *data = vdata;
 	oidcpy(&data->expand->oid, oid);
-	batch_object_write(NULL, data->scratch, data->opt, data->expand);
+	batch_object_write(NULL, data->scratch, data->err, data->opt, data->expand);
 	return 0;
 }
 
@@ -364,6 +366,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
+	struct strbuf err = STRBUF_INIT;
 	struct strbuf format = STRBUF_INIT;
 	struct expand_data data;
 	int save_warning;
@@ -392,6 +395,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 		cb.opt = batch;
 		cb.expand = &data;
 		cb.scratch = &output;
+		cb.err = &err;
 
 		if (batch->unordered) {
 			struct oidset seen = OIDSET_INIT;
@@ -416,6 +420,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 
 		strbuf_release(&format);
 		strbuf_release(&output);
+		strbuf_release(&err);
 		return 0;
 	}
 
@@ -444,12 +449,13 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 			data.rest = p;
 		}
 
-		batch_one_object(input.buf, &output, batch, &data);
+		batch_one_object(input.buf, &output, &err, batch, &data);
 	}
 
 	strbuf_release(&format);
 	strbuf_release(&input);
 	strbuf_release(&output);
+	strbuf_release(&err);
 	warn_on_object_refname_ambiguity = save_warning;
 	return retval;
 }
Signature · 1 line
gitgitgadget
#75 lore raw

PATCH v4 14/14 cat-file: re-implement --textconv, --filters options

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

After cat-file reuses the ref-filter logic, we re-implement the functions of --textconv and --filters options.

Add members use_textconv and use_filters in struct ref_format, and use global variables use_filters and use_textconv in ref-filter.c, so that we can filter the content of the object in get_object(). Use actual_oi to record the real expand_data:
it may point to the original oi or the act_oi processed by textconv_object() or convert_to_working_tree(). grab_values() will grab the contents of actual_oi and grab_common_values() to grab the contents of origin oi, this ensures that %(objectsize) still uses the size of the unfiltered data.

In get_object(), we made an optimization: Firstly, get the size and type of the object instead of directly getting the object data. If using --textconv, after successfully obtaining the filtered object data, an extra oid_object_info_extended() will be skipped, which can reduce the cost of object data copy; If using --filter, the data of the object first will be getted first, and then convert_to_working_tree() will be used to get the filtered object data.

---
 builtin/cat-file.c |  6 +++++
 ref-filter.c       | 59 ++++++++++++++++++++++++++++++++++++++++++++--
 ref-filter.h       |  2 ++
 3 files changed, 65 insertions(+), 2 deletions(-)
builtin/cat-file.c +6 -0
index 27403326e7a7..f3140d927f7d 100644
@@ -380,6 +380,12 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 	if (batch->print_contents)
 		strbuf_addstr(&format, "\n%(raw)");
 	batch->format.format = format.buf;
+
+	if (batch->cmdmode == 'c')
+		batch->format.use_textconv = 1;
+	else if (batch->cmdmode == 'w')
+		batch->format.use_filters = 1;
+
 	if (verify_ref_format(&batch->format))
 		usage_with_options(cat_file_usage, options);
 
ref-filter.c +57 -2

101 lines not on this page · raw message

ref-filter.h +2 -0
index 053980a6a426..497e3e93632f 100644
@@ -80,6 +80,8 @@ struct ref_format {
 	const char *rest;
 	int cat_file_mode;
 	int quote_style;
+	int use_textconv;
+	int use_filters;
 	int use_rest;
 	int use_color;
 
Signature · 1 line
gitgitgadget
#76 lore raw

v5 cover cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic.

Change from last version:

1. At the suggestion of Bagas Sanjaya, modified the expression of submitted information.
2. Remove grab_oid() function in ref-filter to reduce repeated checks.

ZheNing Hu (15):
[GSOC] ref-filter: add obj-type check in grab contents [GSOC] ref-filter: add %(raw) atom
[GSOC] ref-filter: --format=%(raw) re-support --perl [GSOC] ref-filter: use non-const ref_format in *_atom_parser() [GSOC] ref-filter: add %(rest) atom
[GSOC] ref-filter: pass get_object() return value to their callers [GSOC] ref-filter: introduce free_ref_array_item_value() function [GSOC] ref-filter: add cat_file_mode in struct ref_format [GSOC] ref-filter: modify the error message and value in get_object [GSOC] cat-file: add has_object_file() check
[GSOC] cat-file: change batch_objects parameter name [GSOC] cat-file: reuse ref-filter logic
[GSOC] cat-file: reuse err buf in batch_object_write() [GSOC] cat-file: re-implement --textconv, --filters options [GSOC] ref-filter: remove grab_oid() function

 Documentation/git-cat-file.txt     |   6 +
 Documentation/git-for-each-ref.txt |   9 +
 builtin/cat-file.c                 | 277 ++++++----------------
 builtin/tag.c                      |   2 +-
 quote.c                            |  17 ++
 quote.h                            |   1 +
 ref-filter.c                       | 357 ++++++++++++++++++++++-------
 ref-filter.h                       |  14 +-
 t/t1006-cat-file.sh                | 252 ++++++++++++++++++++
 t/t3203-branch-output.sh           |   4 +
 t/t6300-for-each-ref.sh            | 235 +++++++++++++++++++
 t/t6301-for-each-ref-errors.sh     |   2 +-
 t/t7004-tag.sh                     |   4 +
 t/t7030-verify-tag.sh              |   4 +
 14 files changed, 888 insertions(+), 296 deletions(-)

base-commit: 1197f1a46360d3ae96bd9c15908a3a6f8e562207
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-980%2Fadlternative%2Fcat-file-batch-refactor-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-980/adlternative/cat-file-batch-refactor-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/980

Range-diff vs v4:

  1:  f72ad9cc5e8 =  1:  f72ad9cc5e8 [GSOC] ref-filter: add obj-type check in grab contents
  2:  ab497d66c11 !  2:  4e473838b9d [GSOC] ref-filter: add %(raw) atom

@@ Commit message
can help us add raw object data to the buffer or compare two buffers which contain raw object data.

- Beyond, --format=%(raw) cannot be used with --python, --shell,
- --tcl, --perl because if our binary raw data is passed to a
- variable in the host language, the host language may not support
- arbitrary binary data in the variables of its string type.
+ Note that --format=%(raw) cannot be used with --python, --shell,
+ --tcl, and --perl because if the binary raw data is passed to a
+ variable in such languages, these may not support arbitrary binary data
+ in their string variable type.

  3:  b54dbc431e0 =  3:  765cf08a108 [GSOC] ref-filter: --format=%(raw) re-support --perl
  4:  9fbbb3c492f =  4:  d2aeafd0ef3 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()
  5:  08aa44e5e57 =  5:  1ca3a42f041 [GSOC] ref-filter: add %(rest) atom
  6:  05682bccf9f !  6:  67f1a3cca9a [GSOC] ref-filter: pass get_object() return value to their callers

@@ Metadata
## Commit message ##
[GSOC] ref-filter: pass get_object() return value to their callers

- Since in the refactor of git cat-file --batch later,
+ Because in the refactor of git cat-file --batch later,
oid_object_info_extended() in get_object() will be used to obtain the info of an object with it's oid. When the object cannot be obtained in the git repository, cat-file --batch expects to output

  7:  06db6cd6f1f =  7:  2a48a48e81c [GSOC] ref-filter: introduce free_ref_array_item_value() function
  8:  b0d9e139935 =  8:  be55005be75 [GSOC] ref-filter: add cat_file_mode in struct ref_format
  9:  db7dd8b042c =  9:  937f88b7837 [GSOC] ref-filter: modify the error message and value in get_object
 10:  6b577969734 = 10:  45657499c55 [GSOC] cat-file: add has_object_file() check
 11:  069aa203666 ! 11:  bf5c0a017ad [GSOC] cat-file: change batch_objects parameter name

@@ Metadata
## Commit message ##
[GSOC] cat-file: change batch_objects parameter name

- Since cat-file reuses ref-filter logic later will add the
- formal parameter "const struct option *options" to
- batch_objects(), the two synonymous parameters of "opt"
- and "options" may confuse readers, so change batch_options
- parameter of batch_objects() from "opt" to "batch".
+ Because later cat-file reuses ref-filter logic that will add
+ parameter "const struct option *options" to batch_objects(),
+ the two synonymous parameters of "opt" and "options" may
+ confuse readers, so change batch_options parameter of
+ batch_objects() from "opt" to "batch".

 12:  258ec0a46c5 = 12:  370101ba65f [GSOC] cat-file: reuse ref-filter logic
 13:  bda6aae9a6c = 13:  69eef47065d [GSOC] cat-file: reuse err buf in batch_object_write()
 14:  d1114a2bd74 = 14:  a7ac037a946 [GSOC] cat-file: re-implement --textconv, --filters options
  -:  ----------- > 15:  843de8864a9 [GSOC] ref-filter: remove grab_oid() function
Signature · 1 line
gitgitgadget
#77 lore raw

PATCH v5 1/15 ref-filter: add obj-type check in grab contents

From: ZheNing Hu <[email protected]>

Only tag and commit objects use grab_sub_body_contents() to grab object contents in the current codebase. We want to teach the function to also handle blobs and trees to get their raw data, without parsing a blob (whose contents looks like a commit or a tag) incorrectly as a commit or a tag.

Skip the block of code that is specific to handling commits and tags early when the given object is of a wrong type to help later addition to handle other types of objects in this function.

---
 ref-filter.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)
ref-filter.c +15 -9
index 4db0e40ff4c..5cee6512fba 100644
@@ -1356,11 +1356,12 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
 }
 
 /* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
 {
 	int i;
 	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
 	size_t sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+	void *buf = data->content;
 
 	for (i = 0; i < used_atom_cnt; i++) {
 		struct used_atom *atom = &used_atom[i];
@@ -1371,10 +1372,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
 			continue;
 		if (deref)
 			name++;
-		if (strcmp(name, "body") &&
-		    !starts_with(name, "subject") &&
-		    !starts_with(name, "trailers") &&
-		    !starts_with(name, "contents"))
+
+		if ((data->type != OBJ_TAG &&
+		     data->type != OBJ_COMMIT) ||
+		    (strcmp(name, "body") &&
+		     !starts_with(name, "subject") &&
+		     !starts_with(name, "trailers") &&
+		     !starts_with(name, "contents")))
 			continue;
 		if (!subpos)
 			find_subpos(buf,
@@ -1438,17 +1442,19 @@ static void fill_missing_values(struct atom_value *val)
  * pointed at by the ref itself; otherwise it is the object the
  * ref (which is a tag) refers to.
  */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
+static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
 {
+	void *buf = data->content;
+
 	switch (obj->type) {
 	case OBJ_TAG:
 		grab_tag_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("tagger", val, deref, buf);
 		break;
 	case OBJ_COMMIT:
 		grab_commit_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("author", val, deref, buf);
 		grab_person("committer", val, deref, buf);
 		break;
@@ -1678,7 +1684,7 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 			return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
 					       oid_to_hex(&oi->oid), ref->refname);
 		}
-		grab_values(ref->value, deref, *obj, oi->content);
+		grab_values(ref->value, deref, *obj, oi);
 	}
 
 	grab_common_values(ref->value, deref, oi);
Signature · 1 line
gitgitgadget
#78 lore raw

PATCH v5 2/15 ref-filter: add %(raw) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add new formatting option %(raw), which will print the raw object data without any changes. It will help further to migrate all cat-file formatting logic from cat-file to ref-filter.

The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).

E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.

Therefore, we need to find a way to record the length of the atom_value's member s. Although strbuf can already record the string and its length, if we want to replace the type of atom_value's member s with strbuf, many places in ref-filter that are filled with dynamically allocated mermory in v->s are not easy to replace. At the same time, we need to check if v->s == NULL in populate_value(), and strbuf cannot easily distinguish NULL and empty strings, but c-style "const char *" can do it. So add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.

Note that --format=%(raw) cannot be used with --python, --shell, --tcl, and --perl because if the binary raw data is passed to a variable in such languages, these may not support arbitrary binary data in their string variable type.

---
 Documentation/git-for-each-ref.txt |   9 ++
 ref-filter.c                       | 139 +++++++++++++++----
 t/t6300-for-each-ref.sh            | 216 +++++++++++++++++++++++++++++
 3 files changed, 337 insertions(+), 27 deletions(-)
Documentation/git-for-each-ref.txt +9 -0
index 2ae2478de70..7f1f0a1ca3b 100644
@@ -235,6 +235,15 @@ and `date` to extract the named component.  For email fields (`authoremail`,
 without angle brackets, and `:localpart` to get the part before the `@` symbol
 out of the trimmed email.
 
+The raw data in an object is `raw`.
+
+raw:size::
+	The raw data size of the object.
+
+Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
+`--perl` because the host language may not support arbitrary binary data in the
+variables of its string type.
+
 The message in a commit or a tag object is `contents`, from which
 `contents:<part>` can be used to extract various parts out of:
 
ref-filter.c +112 -27

293 lines not on this page · raw message

t/t6300-for-each-ref.sh +216 -0

258 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#79 lore raw

Re: [PATCH v5 02/15] [GSOC] ref-filter: add %(raw) atom

Quoted 5 lines · ZheNing Hu via GitGitGadget

Note that --format=%(raw) cannot be used with --python, --shell, --tcl, and --perl because if the binary raw data is passed to a variable in such languages, these may not support arbitrary binary data in their string variable type.

Commit message looks OK, but...

Documentation/git-for-each-ref.txt and `date` to extract the named component. For email fields (`authoremail`, · line 246

Quoted 4 lines
Documentation/git-for-each-ref.txt +4 -0
+Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
+`--perl` because the host language may not support arbitrary binary data in the
+variables of its string type.
+

Seems like out of sync between commit message and the docs change above. Did you mean the (unsupported) host languages are Python, BASH script, TCL/TK, and Perl respectively? If so, the docs should say:

"Note that --format=%(raw) can not be used with --python, --shell, -tcl, and --perl` because such languages may not support arbitrary binary data in their string variable type."

Thanks.

Signature · 1 line
An old man doll... just what I always wanted! - Clara
#80 lore raw

Re: [PATCH v5 02/15] [GSOC] ref-filter: add %(raw) atom

Quoted 10 lines · Bagas Sanjaya <[email protected]> 于2021年6月26日周六 上午8:42写道:
Quoted 5 lines · ZheNing Hu via GitGitGadget

Note that --format=%(raw) cannot be used with --python, --shell, --tcl, and --perl because if the binary raw data is passed to a variable in such languages, these may not support arbitrary binary data in their string variable type.

Commit message looks OK, but...

Quoted 9 lines

Documentation/git-for-each-ref.txt and `date` to extract the named component. For email fields (`authoremail`, · line 246

Quoted 4 lines
Documentation/git-for-each-ref.txt +4 -0
+Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
+`--perl` because the host language may not support arbitrary binary data in the
+variables of its string type.
+

Seems like out of sync between commit message and the docs change above. Did you mean the (unsupported) host languages are Python, BASH script, TCL/TK, and Perl respectively? If so, the docs should say:

Indeed so. I will change them too.

Quoted 8 lines

"Note that --format=%(raw) can not be used with --python, --shell, -tcl, and --perl` because such languages may not support arbitrary binary data in their string variable type."

Thanks.

--
An old man doll... just what I always wanted! - Clara

Thanks.
--
ZheNing Hu

#81 lore raw

PATCH v5 3/15 ref-filter: --format=%(raw) re-support --perl

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because the perl language can handle binary data correctly, add the function perl_quote_buf_with_len(), which can specify the length of the data and prevent the data from being truncated at '\0' to help --format="%(raw)" re-support --perl.

---
 Documentation/git-for-each-ref.txt |  2 +-
 quote.c                            | 17 +++++++++++++++++
 quote.h                            |  1 +
 ref-filter.c                       | 15 +++++++++++----
 t/t6300-for-each-ref.sh            | 19 +++++++++++++++++--
 5 files changed, 47 insertions(+), 7 deletions(-)
Documentation/git-for-each-ref.txt +1 -1
index 7f1f0a1ca3b..ea9b438c16f 100644
@@ -241,7 +241,7 @@ raw:size::
 	The raw data size of the object.
 
 Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
-`--perl` because the host language may not support arbitrary binary data in the
+because the host language may not support arbitrary binary data in the
 variables of its string type.
 
 The message in a commit or a tag object is `contents`, from which
quote.c +17 -0
index 8a3a5e39eb1..26719d21d1e 100644
@@ -471,6 +471,23 @@ void perl_quote_buf(struct strbuf *sb, const char *src)
 	strbuf_addch(sb, sq);
 }
 
+void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len)
+{
+	const char sq = '\'';
+	const char bq = '\\';
+	const char *c = src;
+	const char *end = src + len;
+
+	strbuf_addch(sb, sq);
+	while (c != end) {
+		if (*c == sq || *c == bq)
+			strbuf_addch(sb, bq);
+		strbuf_addch(sb, *c);
+		c++;
+	}
+	strbuf_addch(sb, sq);
+}
+
 void python_quote_buf(struct strbuf *sb, const char *src)
 {
 	const char sq = '\'';
quote.h +1 -0
index 768cc6338e2..0fe69e264b0 100644
@@ -94,6 +94,7 @@ char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigne
 
 /* quoting as a string literal for other languages */
 void perl_quote_buf(struct strbuf *sb, const char *src);
+void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len);
 void python_quote_buf(struct strbuf *sb, const char *src);
 void tcl_quote_buf(struct strbuf *sb, const char *src);
 void basic_regex_quote_buf(struct strbuf *sb, const char *src);
ref-filter.c +11 -4
index 7822be90307..797b20ffa61 100644
@@ -742,7 +742,10 @@ static void quote_formatting(struct strbuf *s, const char *str, size_t len, int
 		sq_quote_buf(s, str);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(s, str);
+		if (len != ATOM_VALUE_S_SIZE_INIT)
+			perl_quote_buf_with_len(s, str, len);
+		else
+			perl_quote_buf(s, str);
 		break;
 	case QUOTE_PYTHON:
 		python_quote_buf(s, str);
@@ -1006,10 +1009,14 @@ int verify_ref_format(struct ref_format *format)
 		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
 		if (at < 0)
 			die("%s", err.buf);
-		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
-		    used_atom[at].u.raw_data.option == RAW_BARE)
+
+		if ((format->quote_style == QUOTE_PYTHON ||
+		     format->quote_style == QUOTE_SHELL ||
+		     format->quote_style == QUOTE_TCL) &&
+		     used_atom[at].atom_type == ATOM_RAW &&
+		     used_atom[at].u.raw_data.option == RAW_BARE)
 			die(_("--format=%.*s cannot be used with"
-			      "--python, --shell, --tcl, --perl"), (int)(ep - sp - 2), sp + 2);
+			      "--python, --shell, --tcl"), (int)(ep - sp - 2), sp + 2);
 		cp = ep + 1;
 
 		if (skip_prefix(used_atom[at].name, "color:", &color))
t/t6300-for-each-ref.sh +17 -2
index 9c5379e2f56..5556063c347 100755
@@ -915,8 +915,23 @@ test_expect_success '%(raw) with --tcl must fail' '
 	test_must_fail git for-each-ref --format="%(raw)" --tcl
 '
 
-test_expect_success '%(raw) with --perl must fail' '
-	test_must_fail git for-each-ref --format="%(raw)" --perl
+test_expect_success '%(raw) with --perl' '
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob1 --perl | perl > actual &&
+	cmp blob1 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob3 --perl | perl > actual &&
+	cmp blob3 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob8 --perl | perl > actual &&
+	cmp blob8 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/first --perl | perl > actual &&
+	cmp one actual &&
+	git cat-file tree refs/mytrees/first > expected &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/mytrees/first --perl | perl > actual &&
+	cmp expected actual
 '
 
 test_expect_success '%(raw) with --shell must fail' '
Signature · 1 line
gitgitgadget
#82 lore raw

PATCH v5 4/15 ref-filter: use non-const ref_format in *_atom_parser()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Use non-const ref_format in *_atom_parser(), which can help us modify the members of ref_format in *_atom_parser().

---
 builtin/tag.c |  2 +-
 ref-filter.c  | 44 ++++++++++++++++++++++----------------------
 ref-filter.h  |  4 ++--
 3 files changed, 25 insertions(+), 25 deletions(-)
builtin/tag.c +1 -1
index 82fcfc09824..452558ec957 100644
@@ -146,7 +146,7 @@ static int verify_tag(const char *name, const char *ref,
 		      const struct object_id *oid, void *cb_data)
 {
 	int flags;
-	const struct ref_format *format = cb_data;
+	struct ref_format *format = cb_data;
 	flags = GPG_VERIFY_VERBOSE;
 
 	if (format->format)
ref-filter.c +22 -22

164 lines not on this page · raw message

ref-filter.h +2 -2
index baf72a71896..74fb423fc89 100644
@@ -116,7 +116,7 @@ void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
 void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, unsigned int mask, int on);
 /*  Based on the given format and quote_style, fill the strbuf */
 int format_ref_array_item(struct ref_array_item *info,
-			  const struct ref_format *format,
+			  struct ref_format *format,
 			  struct strbuf *final_buf,
 			  struct strbuf *error_buf);
 /*  Parse a single sort specifier and add it to the list */
@@ -137,7 +137,7 @@ void setup_ref_filter_porcelain_msg(void);
  * name must be a fully qualified refname.
  */
 void pretty_print_ref(const char *name, const struct object_id *oid,
-		      const struct ref_format *format);
+		      struct ref_format *format);
 
 /*
  * Push a single ref onto the array; this can be used to construct your own
Signature · 1 line
gitgitgadget
#83 lore raw

PATCH v5 5/15 ref-filter: add %(rest) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let "cat-file --batch=%(rest)" use the ref-filter interface, add %(rest) atom for ref-filter. "git for-each-ref", "git branch", "git tag" and "git verify-tag" will reject %(rest) by default.

---
 ref-filter.c             | 21 +++++++++++++++++++++
 ref-filter.h             |  5 ++++-
 t/t3203-branch-output.sh |  4 ++++
 t/t6300-for-each-ref.sh  |  4 ++++
 t/t7004-tag.sh           |  4 ++++
 t/t7030-verify-tag.sh    |  4 ++++
 6 files changed, 41 insertions(+), 1 deletion(-)
ref-filter.c +21 -0
index d01a0266fb8..10c78de9cfa 100644
@@ -157,6 +157,7 @@ enum atom_type {
 	ATOM_IF,
 	ATOM_THEN,
 	ATOM_ELSE,
+	ATOM_REST,
 };
 
 /*
@@ -559,6 +560,15 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
 	return 0;
 }
 
+static int rest_atom_parser(struct ref_format *format, struct used_atom *atom,
+			    const char *arg, struct strbuf *err)
+{
+	if (arg)
+		return strbuf_addf_ret(err, -1, _("%%(rest) does not take arguments"));
+	format->use_rest = 1;
+	return 0;
+}
+
 static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
 			    const char *arg, struct strbuf *unused_err)
 {
@@ -615,6 +625,7 @@ static struct {
 	[ATOM_IF] = { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
 	[ATOM_THEN] = { "then", SOURCE_NONE },
 	[ATOM_ELSE] = { "else", SOURCE_NONE },
+	[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
 	/*
 	 * Please update $__git_ref_fieldlist in git-completion.bash
 	 * when you add new atoms
@@ -1010,6 +1021,9 @@ int verify_ref_format(struct ref_format *format)
 		if (at < 0)
 			die("%s", err.buf);
 
+		if (used_atom[at].atom_type == ATOM_REST)
+			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+
 		if ((format->quote_style == QUOTE_PYTHON ||
 		     format->quote_style == QUOTE_SHELL ||
 		     format->quote_style == QUOTE_TCL) &&
@@ -1927,6 +1941,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 			v->handler = else_atom_handler;
 			v->s = xstrdup("");
 			continue;
+		} else if (atom_type == ATOM_REST) {
+			if (ref->rest)
+				v->s = xstrdup(ref->rest);
+			else
+				v->s = xstrdup("");
+			continue;
 		} else
 			continue;
 
@@ -2144,6 +2164,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
 
 	FLEX_ALLOC_STR(ref, refname, refname);
 	oidcpy(&ref->objectname, oid);
+	ref->rest = NULL;
 
 	return ref;
 }
ref-filter.h +4 -1
index 74fb423fc89..c15dee8d6b9 100644
@@ -38,6 +38,7 @@ struct ref_sorting {
 
 struct ref_array_item {
 	struct object_id objectname;
+	const char *rest;
 	int flag;
 	unsigned int kind;
 	const char *symref;
@@ -76,14 +77,16 @@ struct ref_format {
 	 * verify_ref_format() afterwards to finalize.
 	 */
 	const char *format;
+	const char *rest;
 	int quote_style;
+	int use_rest;
 	int use_color;
 
 	/* Internal state to ref-filter */
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, 0, -1 }
+#define REF_FORMAT_INIT { .use_color = -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
t/t3203-branch-output.sh +4 -0
index 5325b9f67a0..6e94c6db7b5 100755
@@ -340,6 +340,10 @@ test_expect_success 'git branch --format option' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch with --format=%(rest) must fail' '
+	test_must_fail git branch --format="%(rest)" >actual
+'
+
 test_expect_success 'worktree colors correct' '
 	cat >expect <<-EOF &&
 	* <GREEN>(HEAD detached from fromtag)<RESET>
t/t6300-for-each-ref.sh +4 -0
index 5556063c347..82c0ad2cb11 100755
@@ -1211,6 +1211,10 @@ test_expect_success 'basic atom: head contents:trailers' '
 	test_cmp expect actual.clean
 '
 
+test_expect_success 'basic atom: rest must fail' '
+	test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
+'
+
 test_expect_success 'trailer parsing not fooled by --- line' '
 	git commit --allow-empty -F - <<-\EOF &&
 	this is the subject
t/t7004-tag.sh +4 -0
index 2f72c5c6883..082be85dffc 100755
@@ -1998,6 +1998,10 @@ test_expect_success '--format should list tags as per format given' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git tag -l with --format="%(rest)" must fail' '
+	test_must_fail git tag -l --format="%(rest)" "v1*"
+'
+
 test_expect_success "set up color tests" '
 	echo "<RED>v1.0<RESET>" >expect.color &&
 	echo "v1.0" >expect.bare &&
t/t7030-verify-tag.sh +4 -0
index 3cefde9602b..10faa645157 100755
@@ -194,6 +194,10 @@ test_expect_success GPG 'verifying tag with --format' '
 	test_cmp expect actual
 '
 
+test_expect_success GPG 'verifying tag with --format="%(rest)" must fail' '
+	test_must_fail git verify-tag --format="%(rest)" "fourth-signed"
+'
+
 test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
 	test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
 	test_must_be_empty actual-forged
Signature · 1 line
gitgitgadget
#84 lore raw

PATCH v5 6/15 ref-filter: pass get_object() return value to their callers

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because in the refactor of git cat-file --batch later, oid_object_info_extended() in get_object() will be used to obtain the info of an object with it's oid. When the object cannot be obtained in the git repository, cat-file --batch expects to output "<oid> missing" and continue the next oid query instead of letting Git exit. In other error conditions, Git should exit normally. So we can achieve this function by passing the return value of get_object().

---
 ref-filter.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)
ref-filter.c +11 -6
index 10c78de9cfa..58def6ccd33 100644
@@ -1816,6 +1816,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
 	struct object *obj;
 	int i;
+	int ret = 0;
 	struct object_info empty = OBJECT_INFO_INIT;
 
 	CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -1972,8 +1973,9 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
 
 	oi.oid = ref->objectname;
-	if (get_object(ref, 0, &obj, &oi, err))
-		return -1;
+	ret = get_object(ref, 0, &obj, &oi, err);
+	if (ret)
+		return ret;
 
 	/*
 	 * If there is no atom that wants to know about tagged
@@ -2005,8 +2007,10 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom,
 			      struct atom_value **v, struct strbuf *err)
 {
 	if (!ref->value) {
-		if (populate_value(ref, err))
-			return -1;
+		int ret = populate_value(ref, err);
+
+		if (ret)
+			return ret;
 		fill_missing_values(ref->value);
 	}
 	*v = &ref->value[atom];
@@ -2580,6 +2584,7 @@ int format_ref_array_item(struct ref_array_item *info,
 {
 	const char *cp, *sp, *ep;
 	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+	int ret = 0;
 
 	state.quote_style = format->quote_style;
 	push_stack_element(&state.stack);
@@ -2592,10 +2597,10 @@ int format_ref_array_item(struct ref_array_item *info,
 		if (cp < sp)
 			append_literal(cp, sp, &state);
 		pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-		if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+		if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||
 		    atomv->handler(atomv, &state, error_buf)) {
 			pop_stack_element(&state.stack);
-			return -1;
+			return ret ? ret : -1;
 		}
 	}
 	if (*cp) {
Signature · 1 line
gitgitgadget
#85 lore raw

PATCH v5 7/15 ref-filter: introduce free_ref_array_item_value() function

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

When we use ref_array_item which is not dynamically allocated and want to free the space of its member "value" after the end of use, free_array_item() does not meet our needs, because it tries to free ref_array_item itself and its member "symref".

Introduce free_ref_array_item_value() for freeing ref_array_item value. It will be called internally by free_array_item(), and it will help cat-file --batch free ref_array_item's value memory later.

---
 ref-filter.c | 11 ++++++++---
 ref-filter.h |  2 ++
 2 files changed, 10 insertions(+), 3 deletions(-)
ref-filter.c +8 -3
index 58def6ccd33..22315d4809d 100644
@@ -2291,16 +2291,21 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 	return 0;
 }
 
-/*  Free memory allocated for a ref_array_item */
-static void free_array_item(struct ref_array_item *item)
+void free_ref_array_item_value(struct ref_array_item *item)
 {
-	free((char *)item->symref);
 	if (item->value) {
 		int i;
 		for (i = 0; i < used_atom_cnt; i++)
 			free((char *)item->value[i].s);
 		free(item->value);
 	}
+}
+
+/*  Free memory allocated for a ref_array_item */
+static void free_array_item(struct ref_array_item *item)
+{
+	free((char *)item->symref);
+	free_ref_array_item_value(item);
 	free(item);
 }
 
ref-filter.h +2 -0
index c15dee8d6b9..44e6dc05ac2 100644
@@ -111,6 +111,8 @@ struct ref_format {
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
 /*  Clear all memory allocated to ref_array */
 void ref_array_clear(struct ref_array *array);
+/* Free ref_array_item's value */
+void free_ref_array_item_value(struct ref_array_item *item);
 /*  Used to verify if the given format is correct and to parse out the used atoms */
 int verify_ref_format(struct ref_format *format);
 /*  Sort the given ref_array as per the ref_sorting provided */
Signature · 1 line
gitgitgadget
#86 lore raw

PATCH v5 8/15 ref-filter: add cat_file_mode in struct ref_format

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add cat_file_mode member in struct ref_format, when cat-file --batch use ref-filter logic later, it can help us reject atoms in verify_ref_format() which cat-file cannot use, e.g. %(refname), %(push), %(upstream)...

---
 ref-filter.c | 11 +++++++++--
 ref-filter.h |  1 +
 2 files changed, 10 insertions(+), 2 deletions(-)
ref-filter.c +9 -2
index 22315d4809d..f21f41df0d8 100644
@@ -1021,8 +1021,15 @@ int verify_ref_format(struct ref_format *format)
 		if (at < 0)
 			die("%s", err.buf);
 
-		if (used_atom[at].atom_type == ATOM_REST)
-			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+		if ((!format->cat_file_mode && used_atom[at].atom_type == ATOM_REST) ||
+		    (format->cat_file_mode && (used_atom[at].atom_type == ATOM_FLAG ||
+					       used_atom[at].atom_type == ATOM_HEAD ||
+					       used_atom[at].atom_type == ATOM_PUSH ||
+					       used_atom[at].atom_type == ATOM_REFNAME ||
+					       used_atom[at].atom_type == ATOM_SYMREF ||
+					       used_atom[at].atom_type == ATOM_UPSTREAM ||
+					       used_atom[at].atom_type == ATOM_WORKTREEPATH)))
+			die(_("this command reject atom %%(%.*s)"), (int)(ep - sp - 2), sp + 2);
 
 		if ((format->quote_style == QUOTE_PYTHON ||
 		     format->quote_style == QUOTE_SHELL ||
ref-filter.h +1 -0
index 44e6dc05ac2..053980a6a42 100644
@@ -78,6 +78,7 @@ struct ref_format {
 	 */
 	const char *format;
 	const char *rest;
+	int cat_file_mode;
 	int quote_style;
 	int use_rest;
 	int use_color;
Signature · 1 line
gitgitgadget
#87 lore raw

PATCH v5 9/15 ref-filter: modify the error message and value in get_object

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Let get_object() return 1 and print "<oid> missing" instead of returning -1 and printing "missing object <oid> for <refname>" if oid_object_info_extended() unable to find the data corresponding to oid. When cat-file --batch use ref-filter logic later it can help format_ref_array_item() just report that the object is missing without letting Git exit.

---
 ref-filter.c                   | 4 ++--
 t/t6301-for-each-ref-errors.sh | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
ref-filter.c +2 -2
index f21f41df0d8..181d99c9273 100644
@@ -1749,8 +1749,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 	}
 	if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
 				     OBJECT_INFO_LOOKUP_REPLACE))
-		return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
-				       oid_to_hex(&oi->oid), ref->refname);
+		return strbuf_addf_ret(err, 1, _("%s missing"),
+				       oid_to_hex(&oi->oid));
 	if (oi->info.disk_sizep && oi->disk_size < 0)
 		BUG("Object size is less than zero.");
 
t/t6301-for-each-ref-errors.sh +1 -1
index 40edf9dab53..3553f84a00c 100755
@@ -41,7 +41,7 @@ test_expect_success 'Missing objects are reported correctly' '
 	r=refs/heads/missing &&
 	echo $MISSING >.git/$r &&
 	test_when_finished "rm -f .git/$r" &&
-	echo "fatal: missing object $MISSING for $r" >missing-err &&
+	echo "fatal: $MISSING missing" >missing-err &&
 	test_must_fail git for-each-ref 2>err &&
 	test_cmp missing-err err &&
 	(
Signature · 1 line
gitgitgadget
#88 lore raw

PATCH v5 10/15 cat-file: add has_object_file() check

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Use has_object_file() in batch_one_object() to check whether the input object exists. This can help us reject the missing oid when we let cat-file --batch use ref-filter logic later.

---
 builtin/cat-file.c | 7 +++++++
 1 file changed, 7 insertions(+)
builtin/cat-file.c +7 -0
index 5ebf13359e8..9fd3c04ff20 100644
@@ -428,6 +428,13 @@ static void batch_one_object(const char *obj_name,
 		return;
 	}
 
+	if (!has_object_file(&data->oid)) {
+		printf("%s missing\n",
+		       obj_name ? obj_name : oid_to_hex(&data->oid));
+		fflush(stdout);
+		return;
+	}
+
 	batch_object_write(obj_name, scratch, opt, data);
 }
 
Signature · 1 line
gitgitgadget
#89 lore raw

PATCH v5 11/15 cat-file: change batch_objects parameter name

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because later cat-file reuses ref-filter logic that will add parameter "const struct option *options" to batch_objects(), the two synonymous parameters of "opt" and "options" may confuse readers, so change batch_options parameter of batch_objects() from "opt" to "batch".

---
 builtin/cat-file.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
builtin/cat-file.c +11 -11
index 9fd3c04ff20..cd84c39df96 100644
@@ -495,7 +495,7 @@ static int batch_unordered_packed(const struct object_id *oid,
 	return batch_unordered_object(oid, data);
 }
 
-static int batch_objects(struct batch_options *opt)
+static int batch_objects(struct batch_options *batch)
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
@@ -503,8 +503,8 @@ static int batch_objects(struct batch_options *opt)
 	int save_warning;
 	int retval = 0;
 
-	if (!opt->format)
-		opt->format = "%(objectname) %(objecttype) %(objectsize)";
+	if (!batch->format)
+		batch->format = "%(objectname) %(objecttype) %(objectsize)";
 
 	/*
 	 * Expand once with our special mark_query flag, which will prime the
@@ -513,13 +513,13 @@ static int batch_objects(struct batch_options *opt)
 	 */
 	memset(&data, 0, sizeof(data));
 	data.mark_query = 1;
-	strbuf_expand(&output, opt->format, expand_format, &data);
+	strbuf_expand(&output, batch->format, expand_format, &data);
 	data.mark_query = 0;
 	strbuf_release(&output);
-	if (opt->cmdmode)
+	if (batch->cmdmode)
 		data.split_on_whitespace = 1;
 
-	if (opt->all_objects) {
+	if (batch->all_objects) {
 		struct object_info empty = OBJECT_INFO_INIT;
 		if (!memcmp(&data.info, &empty, sizeof(empty)))
 			data.skip_object_info = 1;
@@ -529,20 +529,20 @@ static int batch_objects(struct batch_options *opt)
 	 * 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 (opt->print_contents)
+	if (batch->print_contents)
 		data.info.typep = &data.type;
 
-	if (opt->all_objects) {
+	if (batch->all_objects) {
 		struct object_cb_data cb;
 
 		if (has_promisor_remote())
 			warning("This repository uses promisor remotes. Some objects may not be loaded.");
 
-		cb.opt = opt;
+		cb.opt = batch;
 		cb.expand = &data;
 		cb.scratch = &output;
 
-		if (opt->unordered) {
+		if (batch->unordered) {
 			struct oidset seen = OIDSET_INIT;
 
 			cb.seen = &seen;
@@ -592,7 +592,7 @@ static int batch_objects(struct batch_options *opt)
 			data.rest = p;
 		}
 
-		batch_one_object(input.buf, &output, opt, &data);
+		batch_one_object(input.buf, &output, batch, &data);
 	}
 
 	strbuf_release(&input);
Signature · 1 line
gitgitgadget
#90 lore raw

PATCH v5 12/15 cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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

Quoted 1 line

/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".

---
 Documentation/git-cat-file.txt |   6 +
 builtin/cat-file.c             | 244 ++++++-------------------------
 t/t1006-cat-file.sh            | 252 +++++++++++++++++++++++++++++++++
 3 files changed, 305 insertions(+), 197 deletions(-)
Documentation/git-cat-file.txt +6 -0
index 4eb0421b3fd..ef8ab952b2f 100644
@@ -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)`.
 
builtin/cat-file.c +47 -197

339 lines not on this page · raw message

t/t1006-cat-file.sh +252 -0

256 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#91 lore raw

Hi,

Quoted 9 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

builtin/cat-file.c +6 -0
+       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.

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 240

Quoted 10 lines
builtin/cat-file.c +8 -0
+                */
+               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);
 }

I think you can get rid of braces in condition ret < 0:

```
        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);
        }
```

Thanks,
Hariom.

#92 lore raw
Quoted 25 lines · Hariom verma <[email protected]> 于2021年6月27日周日 上午1:27写道:

Hi,

Quoted 9 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

builtin/cat-file.c +6 -0
+       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.

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 240

Quoted 10 lines
builtin/cat-file.c +8 -0
+                */
+               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);
 }
Quoted 2 lines

I think you can get rid of braces in condition ret < 0:

Make sences. ;-)

Quoted 16 lines
```
        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);
        }
```
Quoted 2 lines

Thanks,
Hariom.

Thanks,
ZheNing Hu

#93 lore raw
In reply to ZheNing Hu via GitGitGadget
Quoted 8 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

static void batch_object_write(const char *obj_name,
struct strbuf *scratch, struct batch_options *opt, struct expand_data *data)
{

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 218

Quoted 9 lines
builtin/cat-file.c +1 -8
-       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;

No need to initialize ret with 0. Later we are going to assign it with the return value of format_ref_array_item() anyway.

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 224

Quoted 12 lines
builtin/cat-file.c +3 -6
+       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);

Here.

Signature · 1 line
Hariom
#94 lore raw
Quoted 21 lines · Hariom verma <[email protected]> 于2021年6月27日周日 上午2:08写道:
Quoted 8 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

static void batch_object_write(const char *obj_name,
struct strbuf *scratch, struct batch_options *opt, struct expand_data *data)
{

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 218

Quoted 9 lines
builtin/cat-file.c +1 -8
-       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;
Quoted 3 lines

No need to initialize ret with 0. Later we are going to assign it with the return value of format_ref_array_item() anyway.

I agree. It is worth noting that there are similar int ret = 0 in ref-filter.c,
they should be changed too.

Quoted 13 lines

builtin/cat-file.c static void batch_write(struct batch_options *opt, const void *data, int len) · line 224

Quoted 12 lines
builtin/cat-file.c +3 -6
+       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);
Quoted 4 lines

Here.

--
Hariom

--
ZheNing Hu

#95 lore raw

PATCH v5 13/15 cat-file: reuse err buf in batch_object_write()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Reuse the err buffer in batch_object_write(), as the buffer scratch does. This will reduce the overhead of multiple allocations of memory of the err buffer.

---
 builtin/cat-file.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
builtin/cat-file.c +14 -8
index 0e7ad038e5f..27403326e7a 100644
@@ -212,35 +212,36 @@ static void batch_write(struct batch_options *opt, const void *data, int len)
 
 static void batch_object_write(const char *obj_name,
 			       struct strbuf *scratch,
+			       struct strbuf *err,
 			       struct batch_options *opt,
 			       struct expand_data *data)
 {
 	int ret = 0;
-	struct strbuf err = STRBUF_INIT;
 	struct ref_array_item item = { data->oid, data->rest };
 
 	strbuf_reset(scratch);
+	strbuf_reset(err);
 
-	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+	ret = format_ref_array_item(&item, &opt->format, scratch, err);
 	if (ret < 0) {
-		die("%s\n", err.buf);
+		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);
+		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,
 			     struct strbuf *scratch,
+			     struct strbuf *err,
 			     struct batch_options *opt,
 			     struct expand_data *data)
 {
@@ -294,7 +295,7 @@ static void batch_one_object(const char *obj_name,
 		return;
 	}
 
-	batch_object_write(obj_name, scratch, opt, data);
+	batch_object_write(obj_name, scratch, err, opt, data);
 }
 
 struct object_cb_data {
@@ -302,13 +303,14 @@ struct object_cb_data {
 	struct expand_data *expand;
 	struct oidset *seen;
 	struct strbuf *scratch;
+	struct strbuf *err;
 };
 
 static int batch_object_cb(const struct object_id *oid, void *vdata)
 {
 	struct object_cb_data *data = vdata;
 	oidcpy(&data->expand->oid, oid);
-	batch_object_write(NULL, data->scratch, data->opt, data->expand);
+	batch_object_write(NULL, data->scratch, data->err, data->opt, data->expand);
 	return 0;
 }
 
@@ -364,6 +366,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
+	struct strbuf err = STRBUF_INIT;
 	struct strbuf format = STRBUF_INIT;
 	struct expand_data data;
 	int save_warning;
@@ -392,6 +395,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 		cb.opt = batch;
 		cb.expand = &data;
 		cb.scratch = &output;
+		cb.err = &err;
 
 		if (batch->unordered) {
 			struct oidset seen = OIDSET_INIT;
@@ -416,6 +420,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 
 		strbuf_release(&format);
 		strbuf_release(&output);
+		strbuf_release(&err);
 		return 0;
 	}
 
@@ -444,12 +449,13 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 			data.rest = p;
 		}
 
-		batch_one_object(input.buf, &output, batch, &data);
+		batch_one_object(input.buf, &output, &err, batch, &data);
 	}
 
 	strbuf_release(&format);
 	strbuf_release(&input);
 	strbuf_release(&output);
+	strbuf_release(&err);
 	warn_on_object_refname_ambiguity = save_warning;
 	return retval;
 }
Signature · 1 line
gitgitgadget
#96 lore raw

PATCH v5 14/15 cat-file: re-implement --textconv, --filters options

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

After cat-file reuses the ref-filter logic, we re-implement the functions of --textconv and --filters options.

Add members use_textconv and use_filters in struct ref_format, and use global variables use_filters and use_textconv in ref-filter.c, so that we can filter the content of the object in get_object(). Use actual_oi to record the real expand_data:
it may point to the original oi or the act_oi processed by textconv_object() or convert_to_working_tree(). grab_values() will grab the contents of actual_oi and grab_common_values() to grab the contents of origin oi, this ensures that %(objectsize) still uses the size of the unfiltered data.

In get_object(), we made an optimization: Firstly, get the size and type of the object instead of directly getting the object data. If using --textconv, after successfully obtaining the filtered object data, an extra oid_object_info_extended() will be skipped, which can reduce the cost of object data copy; If using --filter, the data of the object first will be getted first, and then convert_to_working_tree() will be used to get the filtered object data.

---
 builtin/cat-file.c |  6 +++++
 ref-filter.c       | 59 ++++++++++++++++++++++++++++++++++++++++++++--
 ref-filter.h       |  2 ++
 3 files changed, 65 insertions(+), 2 deletions(-)
builtin/cat-file.c +6 -0
index 27403326e7a..f3140d927f7 100644
@@ -380,6 +380,12 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 	if (batch->print_contents)
 		strbuf_addstr(&format, "\n%(raw)");
 	batch->format.format = format.buf;
+
+	if (batch->cmdmode == 'c')
+		batch->format.use_textconv = 1;
+	else if (batch->cmdmode == 'w')
+		batch->format.use_filters = 1;
+
 	if (verify_ref_format(&batch->format))
 		usage_with_options(cat_file_usage, options);
 
ref-filter.c +57 -2

101 lines not on this page · raw message

ref-filter.h +2 -0
index 053980a6a42..497e3e93632 100644
@@ -80,6 +80,8 @@ struct ref_format {
 	const char *rest;
 	int cat_file_mode;
 	int quote_style;
+	int use_textconv;
+	int use_filters;
 	int use_rest;
 	int use_color;
 
Signature · 1 line
gitgitgadget
#97 lore raw

PATCH v5 15/15 ref-filter: remove grab_oid() function

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because "atom_type == ATOM_OBJECTNAME" implies the condition of starts_with(name, "objectname"), "atom_type == ATOM_TREE" implies the condition of starts_with(name, "tree"), so the check for starts_with(name, field) in grab_oid() is redundant.

So Remove the grab_oid() from ref-filter, to reduce repeated check.

---
 ref-filter.c | 26 +++++++++-----------------
 1 file changed, 9 insertions(+), 17 deletions(-)
ref-filter.c +9 -17
index 99b87742b0f..ab53c1fd22f 100644
@@ -1071,16 +1071,6 @@ static const char *do_grab_oid(const char *field, const struct object_id *oid,
 	}
 }
 
-static int grab_oid(const char *name, const char *field, const struct object_id *oid,
-		    struct atom_value *v, struct used_atom *atom)
-{
-	if (starts_with(name, field)) {
-		v->s = xstrdup(do_grab_oid(field, oid, atom));
-		return 1;
-	}
-	return 0;
-}
-
 /* See grab_values */
 static void grab_common_values(struct atom_value *val, int deref, struct expand_data *oi)
 {
@@ -1106,8 +1096,9 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
 			}
 		} else if (atom_type == ATOM_DELTABASE)
 			v->s = xstrdup(oid_to_hex(&oi->delta_base_oid));
-		else if (atom_type == ATOM_OBJECTNAME && deref)
-			grab_oid(name, "objectname", &oi->oid, v, &used_atom[i]);
+		else if (atom_type == ATOM_OBJECTNAME && deref) {
+			v->s = xstrdup(do_grab_oid("objectname", &oi->oid, &used_atom[i]));
+		}
 	}
 }
 
@@ -1148,9 +1139,10 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
 			continue;
 		if (deref)
 			name++;
-		if (atom_type == ATOM_TREE &&
-		    grab_oid(name, "tree", get_commit_tree_oid(commit), v, &used_atom[i]))
+		if (atom_type == ATOM_TREE) {
+			v->s = xstrdup(do_grab_oid("tree", get_commit_tree_oid(commit), &used_atom[i]));
 			continue;
+		}
 		if (atom_type == ATOM_NUMPARENT) {
 			v->value = commit_list_count(commit->parents);
 			v->s = xstrfmt("%lu", (unsigned long)v->value);
@@ -1971,9 +1963,9 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 				v->s = xstrdup(buf + 1);
 			}
 			continue;
-		} else if (!deref && atom_type == ATOM_OBJECTNAME &&
-			   grab_oid(name, "objectname", &ref->objectname, v, atom)) {
-				continue;
+		} else if (!deref && atom_type == ATOM_OBJECTNAME) {
+			   v->s = xstrdup(do_grab_oid("objectname", &ref->objectname, atom));
+			   continue;
 		} else if (atom_type == ATOM_HEAD) {
 			if (atom->u.head && !strcmp(ref->refname, atom->u.head))
 				v->s = xstrdup("*");
Signature · 1 line
gitgitgadget
#98 lore raw

v6 cover cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic.

Change from last version:

1. Amend part of the description of git for-each-ref.txt.
2. Modify the code style.
3. Do not assign the 0 to the variable ret during it's initialization.

ZheNing Hu (15):
[GSOC] ref-filter: add obj-type check in grab contents [GSOC] ref-filter: add %(raw) atom
[GSOC] ref-filter: --format=%(raw) re-support --perl [GSOC] ref-filter: use non-const ref_format in *_atom_parser() [GSOC] ref-filter: add %(rest) atom
[GSOC] ref-filter: pass get_object() return value to their callers [GSOC] ref-filter: introduce free_ref_array_item_value() function [GSOC] ref-filter: add cat_file_mode in struct ref_format [GSOC] ref-filter: modify the error message and value in get_object [GSOC] cat-file: add has_object_file() check
[GSOC] cat-file: change batch_objects parameter name [GSOC] cat-file: reuse ref-filter logic
[GSOC] cat-file: reuse err buf in batch_object_write() [GSOC] cat-file: re-implement --textconv, --filters options [GSOC] ref-filter: remove grab_oid() function

 Documentation/git-cat-file.txt     |   6 +
 Documentation/git-for-each-ref.txt |   9 +
 builtin/cat-file.c                 | 277 ++++++----------------
 builtin/tag.c                      |   2 +-
 quote.c                            |  17 ++
 quote.h                            |   1 +
 ref-filter.c                       | 357 ++++++++++++++++++++++-------
 ref-filter.h                       |  14 +-
 t/t1006-cat-file.sh                | 252 ++++++++++++++++++++
 t/t3203-branch-output.sh           |   4 +
 t/t6300-for-each-ref.sh            | 235 +++++++++++++++++++
 t/t6301-for-each-ref-errors.sh     |   2 +-
 t/t7004-tag.sh                     |   4 +
 t/t7030-verify-tag.sh              |   4 +
 14 files changed, 888 insertions(+), 296 deletions(-)

base-commit: 1197f1a46360d3ae96bd9c15908a3a6f8e562207
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-980%2Fadlternative%2Fcat-file-batch-refactor-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-980/adlternative/cat-file-batch-refactor-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/980

Range-diff vs v5:

  1:  f72ad9cc5e8 =  1:  f72ad9cc5e8 [GSOC] ref-filter: add obj-type check in grab contents
  2:  4e473838b9d !  2:  d9bc50c4ae6 [GSOC] ref-filter: add %(raw) atom
     @@ Commit message
      
          Mentored-by: Christian Couder <[email protected]>
          Mentored-by: Hariom Verma <[email protected]>
     +    Helped-by: Bagas Sanjaya <[email protected]>
          Helped-by: Ævar Arnfjörð Bjarmason <[email protected]>
          Helped-by: Felipe Contreras <[email protected]>
          Helped-by: Phillip Wood <[email protected]>
     @@ Documentation/git-for-each-ref.txt: and `date` to extract the named component.
      +	The raw data size of the object.
      +
      +Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
     -+`--perl` because the host language may not support arbitrary binary data in the
     -+variables of its string type.
     ++`--perl` because the such language may not support arbitrary binary data in their
     ++string variable type.
      +
       The message in a commit or a tag object is `contents`, from which
       `contents:<part>` can be used to extract various parts out of:
     @@ t/t6300-for-each-ref.sh: test_atom refs/myblobs/first contents:body ""
      +	printf "  " >blob7 &&
      +	>blob8 &&
      +	obj=$(git hash-object -w blob1) &&
     -+        git update-ref refs/myblobs/blob1 "$obj" &&
     ++	git update-ref refs/myblobs/blob1 "$obj" &&
      +	obj=$(git hash-object -w blob2) &&
     -+        git update-ref refs/myblobs/blob2 "$obj" &&
     ++	git update-ref refs/myblobs/blob2 "$obj" &&
      +	obj=$(git hash-object -w blob3) &&
     -+        git update-ref refs/myblobs/blob3 "$obj" &&
     ++	git update-ref refs/myblobs/blob3 "$obj" &&
      +	obj=$(git hash-object -w blob4) &&
     -+        git update-ref refs/myblobs/blob4 "$obj" &&
     ++	git update-ref refs/myblobs/blob4 "$obj" &&
      +	obj=$(git hash-object -w blob5) &&
     -+        git update-ref refs/myblobs/blob5 "$obj" &&
     ++	git update-ref refs/myblobs/blob5 "$obj" &&
      +	obj=$(git hash-object -w blob6) &&
     -+        git update-ref refs/myblobs/blob6 "$obj" &&
     ++	git update-ref refs/myblobs/blob6 "$obj" &&
      +	obj=$(git hash-object -w blob7) &&
     -+        git update-ref refs/myblobs/blob7 "$obj" &&
     ++	git update-ref refs/myblobs/blob7 "$obj" &&
      +	obj=$(git hash-object -w blob8) &&
     -+        git update-ref refs/myblobs/blob8 "$obj"
     ++	git update-ref refs/myblobs/blob8 "$obj"
      +'
      +
      +test_expect_success 'Verify sorts with raw' '

3: 765cf08a108 ! 3: 47f868f63d9 [GSOC] ref-filter: --format=%(raw) re-support --perl
@@ Documentation/git-for-each-ref.txt: raw:size::
The raw data size of the object.

Note that --format=%(raw) can not be used with --python, --shell, --tcl,
----perl because the host language may not support arbitrary binary data in the -+because the host language may not support arbitrary binary data in the
- variables of its string type.
+---perl because the such language may not support arbitrary binary data in their ++because the such language may not support arbitrary binary data in their
+ string variable type.

The message in a commit or a tag object is contents, from which

  4:  d2aeafd0ef3 =  4:  debca156470 [GSOC] ref-filter: use non-const ref_format in *_atom_parser()
  5:  1ca3a42f041 =  5:  cb0df2b8207 [GSOC] ref-filter: add %(rest) atom
  6:  67f1a3cca9a !  6:  9873354930a [GSOC] ref-filter: pass get_object() return value to their callers
     @@ ref-filter.c: static int populate_value(struct ref_array_item *ref, struct strbu
       {
       	struct object *obj;
       	int i;
     -+	int ret = 0;
     ++	int ret;
       	struct object_info empty = OBJECT_INFO_INIT;
       
       	CALLOC_ARRAY(ref->value, used_atom_cnt);
     @@ ref-filter.c: int format_ref_array_item(struct ref_array_item *info,
       {
       	const char *cp, *sp, *ep;
       	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
     -+	int ret = 0;
     ++	int ret;
       
       	state.quote_style = format->quote_style;
       	push_stack_element(&state.stack);
  7:  2a48a48e81c =  7:  e592c21ea1d [GSOC] ref-filter: introduce free_ref_array_item_value() function
  8:  be55005be75 =  8:  b6e7757de4c [GSOC] ref-filter: add cat_file_mode in struct ref_format
  9:  937f88b7837 =  9:  85686187d49 [GSOC] ref-filter: modify the error message and value in get_object
 10:  45657499c55 = 10:  6037295ee58 [GSOC] cat-file: add has_object_file() check
 11:  bf5c0a017ad = 11:  32e1ca56389 [GSOC] cat-file: change batch_objects parameter name
 12:  370101ba65f ! 12:  9a1f0732940 [GSOC] cat-file: reuse ref-filter logic
     @@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi
      -		fflush(stdout);
      -		return;
      -	}
     -+	int ret = 0;
     ++	int ret;
      +	struct strbuf err = STRBUF_INIT;
      +	struct ref_array_item item = { data->oid, data->rest };
       
     @@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi
      -		print_object_or_die(opt, data);
      -		batch_write(opt, "\n", 1);
      +	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
     -+	if (ret < 0) {
     ++	if (ret < 0)
      +		die("%s\n", err.buf);
     -+	} if (ret) {
     ++	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.

13: 69eef47065d ! 13: 3fb47584924 [GSOC] cat-file: reuse err buf in batch_object_write()
@@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi
struct batch_options *opt,
struct expand_data *data)
{
- int ret = 0;
+ int ret;
- struct strbuf err = STRBUF_INIT;
struct ref_array_item item = { data->oid, data->rest };

@@ builtin/cat-file.c: static void batch_write(struct batch_options *opt, const voi

- ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+ ret = format_ref_array_item(&item, &opt->format, scratch, err);
- if (ret < 0) {
+ if (ret < 0)
- die("%s\n", err.buf);
+ die("%s\n", err->buf);
- } if (ret) {
+ 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.

 14:  a7ac037a946 = 14:  e0b1a05e711 [GSOC] cat-file: re-implement --textconv, --filters options
 15:  843de8864a9 = 15:  891d62fd93f [GSOC] ref-filter: remove grab_oid() function
Signature · 1 line
gitgitgadget
#99 lore raw

PATCH v6 1/15 ref-filter: add obj-type check in grab contents

From: ZheNing Hu <[email protected]>

Only tag and commit objects use grab_sub_body_contents() to grab object contents in the current codebase. We want to teach the function to also handle blobs and trees to get their raw data, without parsing a blob (whose contents looks like a commit or a tag) incorrectly as a commit or a tag.

Skip the block of code that is specific to handling commits and tags early when the given object is of a wrong type to help later addition to handle other types of objects in this function.

---
 ref-filter.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)
ref-filter.c +15 -9
index 4db0e40ff4c..5cee6512fba 100644
@@ -1356,11 +1356,12 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
 }
 
 /* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
 {
 	int i;
 	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
 	size_t sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+	void *buf = data->content;
 
 	for (i = 0; i < used_atom_cnt; i++) {
 		struct used_atom *atom = &used_atom[i];
@@ -1371,10 +1372,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
 			continue;
 		if (deref)
 			name++;
-		if (strcmp(name, "body") &&
-		    !starts_with(name, "subject") &&
-		    !starts_with(name, "trailers") &&
-		    !starts_with(name, "contents"))
+
+		if ((data->type != OBJ_TAG &&
+		     data->type != OBJ_COMMIT) ||
+		    (strcmp(name, "body") &&
+		     !starts_with(name, "subject") &&
+		     !starts_with(name, "trailers") &&
+		     !starts_with(name, "contents")))
 			continue;
 		if (!subpos)
 			find_subpos(buf,
@@ -1438,17 +1442,19 @@ static void fill_missing_values(struct atom_value *val)
  * pointed at by the ref itself; otherwise it is the object the
  * ref (which is a tag) refers to.
  */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
+static void grab_values(struct atom_value *val, int deref, struct object *obj, struct expand_data *data)
 {
+	void *buf = data->content;
+
 	switch (obj->type) {
 	case OBJ_TAG:
 		grab_tag_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("tagger", val, deref, buf);
 		break;
 	case OBJ_COMMIT:
 		grab_commit_values(val, deref, obj);
-		grab_sub_body_contents(val, deref, buf);
+		grab_sub_body_contents(val, deref, data);
 		grab_person("author", val, deref, buf);
 		grab_person("committer", val, deref, buf);
 		break;
@@ -1678,7 +1684,7 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 			return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
 					       oid_to_hex(&oi->oid), ref->refname);
 		}
-		grab_values(ref->value, deref, *obj, oi->content);
+		grab_values(ref->value, deref, *obj, oi);
 	}
 
 	grab_common_values(ref->value, deref, oi);
Signature · 1 line
gitgitgadget
#100 lore raw

PATCH v6 2/15 ref-filter: add %(raw) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add new formatting option %(raw), which will print the raw object data without any changes. It will help further to migrate all cat-file formatting logic from cat-file to ref-filter.

The raw data of blob, tree objects may contain '\0', but most of the logic in ref-filter depends on the output of the atom being text (specifically, no embedded NULs in it).

E.g. quote_formatting() use strbuf_addstr() or *._quote_buf() add the data to the buffer. The raw data of a tree object is 100644 one\0..., only the 100644 one will be added to the buffer, which is incorrect.

Therefore, we need to find a way to record the length of the atom_value's member s. Although strbuf can already record the string and its length, if we want to replace the type of atom_value's member s with strbuf, many places in ref-filter that are filled with dynamically allocated mermory in v->s are not easy to replace. At the same time, we need to check if v->s == NULL in populate_value(), and strbuf cannot easily distinguish NULL and empty strings, but c-style "const char *" can do it. So add a new member in struct atom_value: s_size, which can record raw object size, it can help us add raw object data to the buffer or compare two buffers which contain raw object data.

Note that --format=%(raw) cannot be used with --python, --shell, --tcl, and --perl because if the binary raw data is passed to a variable in such languages, these may not support arbitrary binary data in their string variable type.

---
 Documentation/git-for-each-ref.txt |   9 ++
 ref-filter.c                       | 139 +++++++++++++++----
 t/t6300-for-each-ref.sh            | 216 +++++++++++++++++++++++++++++
 3 files changed, 337 insertions(+), 27 deletions(-)
Documentation/git-for-each-ref.txt +9 -0
index 2ae2478de70..3727a5ffee7 100644
@@ -235,6 +235,15 @@ and `date` to extract the named component.  For email fields (`authoremail`,
 without angle brackets, and `:localpart` to get the part before the `@` symbol
 out of the trimmed email.
 
+The raw data in an object is `raw`.
+
+raw:size::
+	The raw data size of the object.
+
+Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
+`--perl` because the such language may not support arbitrary binary data in their
+string variable type.
+
 The message in a commit or a tag object is `contents`, from which
 `contents:<part>` can be used to extract various parts out of:
 
ref-filter.c +112 -27

293 lines not on this page · raw message

t/t6300-for-each-ref.sh +216 -0

258 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#101 lore raw

Re: [PATCH v6 02/15] [GSOC] ref-filter: add %(raw) atom

Documentation/git-for-each-ref.txt and `date` to extract the named component. For email fields (`authoremail`, · line 246

Quoted 4 lines · ZheNing Hu via GitGitGadget
Documentation/git-for-each-ref.txt +4 -0
+Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
+`--perl` because the such language may not support arbitrary binary data in their
+string variable type.
+

s/the such language/such languages/

Thanks.

Signature · 1 line
An old man doll... just what I always wanted! - Clara
#102 lore raw

PATCH v6 3/15 ref-filter: --format=%(raw) re-support --perl

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because the perl language can handle binary data correctly, add the function perl_quote_buf_with_len(), which can specify the length of the data and prevent the data from being truncated at '\0' to help --format="%(raw)" re-support --perl.

---
 Documentation/git-for-each-ref.txt |  2 +-
 quote.c                            | 17 +++++++++++++++++
 quote.h                            |  1 +
 ref-filter.c                       | 15 +++++++++++----
 t/t6300-for-each-ref.sh            | 19 +++++++++++++++++--
 5 files changed, 47 insertions(+), 7 deletions(-)
Documentation/git-for-each-ref.txt +1 -1
index 3727a5ffee7..6f970088f46 100644
@@ -241,7 +241,7 @@ raw:size::
 	The raw data size of the object.
 
 Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`,
-`--perl` because the such language may not support arbitrary binary data in their
+because the such language may not support arbitrary binary data in their
 string variable type.
 
 The message in a commit or a tag object is `contents`, from which
quote.c +17 -0
index 8a3a5e39eb1..26719d21d1e 100644
@@ -471,6 +471,23 @@ void perl_quote_buf(struct strbuf *sb, const char *src)
 	strbuf_addch(sb, sq);
 }
 
+void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len)
+{
+	const char sq = '\'';
+	const char bq = '\\';
+	const char *c = src;
+	const char *end = src + len;
+
+	strbuf_addch(sb, sq);
+	while (c != end) {
+		if (*c == sq || *c == bq)
+			strbuf_addch(sb, bq);
+		strbuf_addch(sb, *c);
+		c++;
+	}
+	strbuf_addch(sb, sq);
+}
+
 void python_quote_buf(struct strbuf *sb, const char *src)
 {
 	const char sq = '\'';
quote.h +1 -0
index 768cc6338e2..0fe69e264b0 100644
@@ -94,6 +94,7 @@ char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigne
 
 /* quoting as a string literal for other languages */
 void perl_quote_buf(struct strbuf *sb, const char *src);
+void perl_quote_buf_with_len(struct strbuf *sb, const char *src, size_t len);
 void python_quote_buf(struct strbuf *sb, const char *src);
 void tcl_quote_buf(struct strbuf *sb, const char *src);
 void basic_regex_quote_buf(struct strbuf *sb, const char *src);
ref-filter.c +11 -4
index 7822be90307..797b20ffa61 100644
@@ -742,7 +742,10 @@ static void quote_formatting(struct strbuf *s, const char *str, size_t len, int
 		sq_quote_buf(s, str);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(s, str);
+		if (len != ATOM_VALUE_S_SIZE_INIT)
+			perl_quote_buf_with_len(s, str, len);
+		else
+			perl_quote_buf(s, str);
 		break;
 	case QUOTE_PYTHON:
 		python_quote_buf(s, str);
@@ -1006,10 +1009,14 @@ int verify_ref_format(struct ref_format *format)
 		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
 		if (at < 0)
 			die("%s", err.buf);
-		if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
-		    used_atom[at].u.raw_data.option == RAW_BARE)
+
+		if ((format->quote_style == QUOTE_PYTHON ||
+		     format->quote_style == QUOTE_SHELL ||
+		     format->quote_style == QUOTE_TCL) &&
+		     used_atom[at].atom_type == ATOM_RAW &&
+		     used_atom[at].u.raw_data.option == RAW_BARE)
 			die(_("--format=%.*s cannot be used with"
-			      "--python, --shell, --tcl, --perl"), (int)(ep - sp - 2), sp + 2);
+			      "--python, --shell, --tcl"), (int)(ep - sp - 2), sp + 2);
 		cp = ep + 1;
 
 		if (skip_prefix(used_atom[at].name, "color:", &color))
t/t6300-for-each-ref.sh +17 -2
index 18554f62d94..0b66e743c58 100755
@@ -915,8 +915,23 @@ test_expect_success '%(raw) with --tcl must fail' '
 	test_must_fail git for-each-ref --format="%(raw)" --tcl
 '
 
-test_expect_success '%(raw) with --perl must fail' '
-	test_must_fail git for-each-ref --format="%(raw)" --perl
+test_expect_success '%(raw) with --perl' '
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob1 --perl | perl > actual &&
+	cmp blob1 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob3 --perl | perl > actual &&
+	cmp blob3 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/blob8 --perl | perl > actual &&
+	cmp blob8 actual &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/myblobs/first --perl | perl > actual &&
+	cmp one actual &&
+	git cat-file tree refs/mytrees/first > expected &&
+	git for-each-ref --format="\$name= %(raw);
+print \"\$name\"" refs/mytrees/first --perl | perl > actual &&
+	cmp expected actual
 '
 
 test_expect_success '%(raw) with --shell must fail' '
Signature · 1 line
gitgitgadget
#103 lore raw

PATCH v6 4/15 ref-filter: use non-const ref_format in *_atom_parser()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Use non-const ref_format in *_atom_parser(), which can help us modify the members of ref_format in *_atom_parser().

---
 builtin/tag.c |  2 +-
 ref-filter.c  | 44 ++++++++++++++++++++++----------------------
 ref-filter.h  |  4 ++--
 3 files changed, 25 insertions(+), 25 deletions(-)
builtin/tag.c +1 -1
index 82fcfc09824..452558ec957 100644
@@ -146,7 +146,7 @@ static int verify_tag(const char *name, const char *ref,
 		      const struct object_id *oid, void *cb_data)
 {
 	int flags;
-	const struct ref_format *format = cb_data;
+	struct ref_format *format = cb_data;
 	flags = GPG_VERIFY_VERBOSE;
 
 	if (format->format)
ref-filter.c +22 -22

164 lines not on this page · raw message

ref-filter.h +2 -2
index baf72a71896..74fb423fc89 100644
@@ -116,7 +116,7 @@ void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
 void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, unsigned int mask, int on);
 /*  Based on the given format and quote_style, fill the strbuf */
 int format_ref_array_item(struct ref_array_item *info,
-			  const struct ref_format *format,
+			  struct ref_format *format,
 			  struct strbuf *final_buf,
 			  struct strbuf *error_buf);
 /*  Parse a single sort specifier and add it to the list */
@@ -137,7 +137,7 @@ void setup_ref_filter_porcelain_msg(void);
  * name must be a fully qualified refname.
  */
 void pretty_print_ref(const char *name, const struct object_id *oid,
-		      const struct ref_format *format);
+		      struct ref_format *format);
 
 /*
  * Push a single ref onto the array; this can be used to construct your own
Signature · 1 line
gitgitgadget
#104 lore raw

PATCH v6 5/15 ref-filter: add %(rest) atom

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

In order to let "cat-file --batch=%(rest)" use the ref-filter interface, add %(rest) atom for ref-filter. "git for-each-ref", "git branch", "git tag" and "git verify-tag" will reject %(rest) by default.

---
 ref-filter.c             | 21 +++++++++++++++++++++
 ref-filter.h             |  5 ++++-
 t/t3203-branch-output.sh |  4 ++++
 t/t6300-for-each-ref.sh  |  4 ++++
 t/t7004-tag.sh           |  4 ++++
 t/t7030-verify-tag.sh    |  4 ++++
 6 files changed, 41 insertions(+), 1 deletion(-)
ref-filter.c +21 -0
index d01a0266fb8..10c78de9cfa 100644
@@ -157,6 +157,7 @@ enum atom_type {
 	ATOM_IF,
 	ATOM_THEN,
 	ATOM_ELSE,
+	ATOM_REST,
 };
 
 /*
@@ -559,6 +560,15 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
 	return 0;
 }
 
+static int rest_atom_parser(struct ref_format *format, struct used_atom *atom,
+			    const char *arg, struct strbuf *err)
+{
+	if (arg)
+		return strbuf_addf_ret(err, -1, _("%%(rest) does not take arguments"));
+	format->use_rest = 1;
+	return 0;
+}
+
 static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
 			    const char *arg, struct strbuf *unused_err)
 {
@@ -615,6 +625,7 @@ static struct {
 	[ATOM_IF] = { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
 	[ATOM_THEN] = { "then", SOURCE_NONE },
 	[ATOM_ELSE] = { "else", SOURCE_NONE },
+	[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
 	/*
 	 * Please update $__git_ref_fieldlist in git-completion.bash
 	 * when you add new atoms
@@ -1010,6 +1021,9 @@ int verify_ref_format(struct ref_format *format)
 		if (at < 0)
 			die("%s", err.buf);
 
+		if (used_atom[at].atom_type == ATOM_REST)
+			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+
 		if ((format->quote_style == QUOTE_PYTHON ||
 		     format->quote_style == QUOTE_SHELL ||
 		     format->quote_style == QUOTE_TCL) &&
@@ -1927,6 +1941,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 			v->handler = else_atom_handler;
 			v->s = xstrdup("");
 			continue;
+		} else if (atom_type == ATOM_REST) {
+			if (ref->rest)
+				v->s = xstrdup(ref->rest);
+			else
+				v->s = xstrdup("");
+			continue;
 		} else
 			continue;
 
@@ -2144,6 +2164,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
 
 	FLEX_ALLOC_STR(ref, refname, refname);
 	oidcpy(&ref->objectname, oid);
+	ref->rest = NULL;
 
 	return ref;
 }
ref-filter.h +4 -1
index 74fb423fc89..c15dee8d6b9 100644
@@ -38,6 +38,7 @@ struct ref_sorting {
 
 struct ref_array_item {
 	struct object_id objectname;
+	const char *rest;
 	int flag;
 	unsigned int kind;
 	const char *symref;
@@ -76,14 +77,16 @@ struct ref_format {
 	 * verify_ref_format() afterwards to finalize.
 	 */
 	const char *format;
+	const char *rest;
 	int quote_style;
+	int use_rest;
 	int use_color;
 
 	/* Internal state to ref-filter */
 	int need_color_reset_at_eol;
 };
 
-#define REF_FORMAT_INIT { NULL, 0, -1 }
+#define REF_FORMAT_INIT { .use_color = -1 }
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
t/t3203-branch-output.sh +4 -0
index 5325b9f67a0..6e94c6db7b5 100755
@@ -340,6 +340,10 @@ test_expect_success 'git branch --format option' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch with --format=%(rest) must fail' '
+	test_must_fail git branch --format="%(rest)" >actual
+'
+
 test_expect_success 'worktree colors correct' '
 	cat >expect <<-EOF &&
 	* <GREEN>(HEAD detached from fromtag)<RESET>
t/t6300-for-each-ref.sh +4 -0
index 0b66e743c58..6ca5c2cc19c 100755
@@ -1211,6 +1211,10 @@ test_expect_success 'basic atom: head contents:trailers' '
 	test_cmp expect actual.clean
 '
 
+test_expect_success 'basic atom: rest must fail' '
+	test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
+'
+
 test_expect_success 'trailer parsing not fooled by --- line' '
 	git commit --allow-empty -F - <<-\EOF &&
 	this is the subject
t/t7004-tag.sh +4 -0
index 2f72c5c6883..082be85dffc 100755
@@ -1998,6 +1998,10 @@ test_expect_success '--format should list tags as per format given' '
 	test_cmp expect actual
 '
 
+test_expect_success 'git tag -l with --format="%(rest)" must fail' '
+	test_must_fail git tag -l --format="%(rest)" "v1*"
+'
+
 test_expect_success "set up color tests" '
 	echo "<RED>v1.0<RESET>" >expect.color &&
 	echo "v1.0" >expect.bare &&
t/t7030-verify-tag.sh +4 -0
index 3cefde9602b..10faa645157 100755
@@ -194,6 +194,10 @@ test_expect_success GPG 'verifying tag with --format' '
 	test_cmp expect actual
 '
 
+test_expect_success GPG 'verifying tag with --format="%(rest)" must fail' '
+	test_must_fail git verify-tag --format="%(rest)" "fourth-signed"
+'
+
 test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
 	test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
 	test_must_be_empty actual-forged
Signature · 1 line
gitgitgadget
#105 lore raw

PATCH v6 6/15 ref-filter: pass get_object() return value to their callers

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because in the refactor of git cat-file --batch later, oid_object_info_extended() in get_object() will be used to obtain the info of an object with it's oid. When the object cannot be obtained in the git repository, cat-file --batch expects to output "<oid> missing" and continue the next oid query instead of letting Git exit. In other error conditions, Git should exit normally. So we can achieve this function by passing the return value of get_object().

---
 ref-filter.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)
ref-filter.c +11 -6
index 10c78de9cfa..e4988aa8a24 100644
@@ -1816,6 +1816,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
 	struct object *obj;
 	int i;
+	int ret;
 	struct object_info empty = OBJECT_INFO_INIT;
 
 	CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -1972,8 +1973,9 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
 
 	oi.oid = ref->objectname;
-	if (get_object(ref, 0, &obj, &oi, err))
-		return -1;
+	ret = get_object(ref, 0, &obj, &oi, err);
+	if (ret)
+		return ret;
 
 	/*
 	 * If there is no atom that wants to know about tagged
@@ -2005,8 +2007,10 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom,
 			      struct atom_value **v, struct strbuf *err)
 {
 	if (!ref->value) {
-		if (populate_value(ref, err))
-			return -1;
+		int ret = populate_value(ref, err);
+
+		if (ret)
+			return ret;
 		fill_missing_values(ref->value);
 	}
 	*v = &ref->value[atom];
@@ -2580,6 +2584,7 @@ int format_ref_array_item(struct ref_array_item *info,
 {
 	const char *cp, *sp, *ep;
 	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+	int ret;
 
 	state.quote_style = format->quote_style;
 	push_stack_element(&state.stack);
@@ -2592,10 +2597,10 @@ int format_ref_array_item(struct ref_array_item *info,
 		if (cp < sp)
 			append_literal(cp, sp, &state);
 		pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
-		if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+		if (pos < 0 || (ret = get_ref_atom_value(info, pos, &atomv, error_buf)) ||
 		    atomv->handler(atomv, &state, error_buf)) {
 			pop_stack_element(&state.stack);
-			return -1;
+			return ret ? ret : -1;
 		}
 	}
 	if (*cp) {
Signature · 1 line
gitgitgadget
#106 lore raw

PATCH v6 7/15 ref-filter: introduce free_ref_array_item_value() function

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

When we use ref_array_item which is not dynamically allocated and want to free the space of its member "value" after the end of use, free_array_item() does not meet our needs, because it tries to free ref_array_item itself and its member "symref".

Introduce free_ref_array_item_value() for freeing ref_array_item value. It will be called internally by free_array_item(), and it will help cat-file --batch free ref_array_item's value memory later.

---
 ref-filter.c | 11 ++++++++---
 ref-filter.h |  2 ++
 2 files changed, 10 insertions(+), 3 deletions(-)
ref-filter.c +8 -3
index e4988aa8a24..731e596eaa6 100644
@@ -2291,16 +2291,21 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 	return 0;
 }
 
-/*  Free memory allocated for a ref_array_item */
-static void free_array_item(struct ref_array_item *item)
+void free_ref_array_item_value(struct ref_array_item *item)
 {
-	free((char *)item->symref);
 	if (item->value) {
 		int i;
 		for (i = 0; i < used_atom_cnt; i++)
 			free((char *)item->value[i].s);
 		free(item->value);
 	}
+}
+
+/*  Free memory allocated for a ref_array_item */
+static void free_array_item(struct ref_array_item *item)
+{
+	free((char *)item->symref);
+	free_ref_array_item_value(item);
 	free(item);
 }
 
ref-filter.h +2 -0
index c15dee8d6b9..44e6dc05ac2 100644
@@ -111,6 +111,8 @@ struct ref_format {
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
 /*  Clear all memory allocated to ref_array */
 void ref_array_clear(struct ref_array *array);
+/* Free ref_array_item's value */
+void free_ref_array_item_value(struct ref_array_item *item);
 /*  Used to verify if the given format is correct and to parse out the used atoms */
 int verify_ref_format(struct ref_format *format);
 /*  Sort the given ref_array as per the ref_sorting provided */
Signature · 1 line
gitgitgadget
#107 lore raw

PATCH v6 8/15 ref-filter: add cat_file_mode in struct ref_format

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Add cat_file_mode member in struct ref_format, when cat-file --batch use ref-filter logic later, it can help us reject atoms in verify_ref_format() which cat-file cannot use, e.g. %(refname), %(push), %(upstream)...

---
 ref-filter.c | 11 +++++++++--
 ref-filter.h |  1 +
 2 files changed, 10 insertions(+), 2 deletions(-)
ref-filter.c +9 -2
index 731e596eaa6..45122959eef 100644
@@ -1021,8 +1021,15 @@ int verify_ref_format(struct ref_format *format)
 		if (at < 0)
 			die("%s", err.buf);
 
-		if (used_atom[at].atom_type == ATOM_REST)
-			die("this command reject atom %%(%.*s)", (int)(ep - sp - 2), sp + 2);
+		if ((!format->cat_file_mode && used_atom[at].atom_type == ATOM_REST) ||
+		    (format->cat_file_mode && (used_atom[at].atom_type == ATOM_FLAG ||
+					       used_atom[at].atom_type == ATOM_HEAD ||
+					       used_atom[at].atom_type == ATOM_PUSH ||
+					       used_atom[at].atom_type == ATOM_REFNAME ||
+					       used_atom[at].atom_type == ATOM_SYMREF ||
+					       used_atom[at].atom_type == ATOM_UPSTREAM ||
+					       used_atom[at].atom_type == ATOM_WORKTREEPATH)))
+			die(_("this command reject atom %%(%.*s)"), (int)(ep - sp - 2), sp + 2);
 
 		if ((format->quote_style == QUOTE_PYTHON ||
 		     format->quote_style == QUOTE_SHELL ||
ref-filter.h +1 -0
index 44e6dc05ac2..053980a6a42 100644
@@ -78,6 +78,7 @@ struct ref_format {
 	 */
 	const char *format;
 	const char *rest;
+	int cat_file_mode;
 	int quote_style;
 	int use_rest;
 	int use_color;
Signature · 1 line
gitgitgadget
#108 lore raw

PATCH v6 9/15 ref-filter: modify the error message and value in get_object

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Let get_object() return 1 and print "<oid> missing" instead of returning -1 and printing "missing object <oid> for <refname>" if oid_object_info_extended() unable to find the data corresponding to oid. When cat-file --batch use ref-filter logic later it can help format_ref_array_item() just report that the object is missing without letting Git exit.

---
 ref-filter.c                   | 4 ++--
 t/t6301-for-each-ref-errors.sh | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
ref-filter.c +2 -2
index 45122959eef..9ca3dd5557d 100644
@@ -1749,8 +1749,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
 	}
 	if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
 				     OBJECT_INFO_LOOKUP_REPLACE))
-		return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
-				       oid_to_hex(&oi->oid), ref->refname);
+		return strbuf_addf_ret(err, 1, _("%s missing"),
+				       oid_to_hex(&oi->oid));
 	if (oi->info.disk_sizep && oi->disk_size < 0)
 		BUG("Object size is less than zero.");
 
t/t6301-for-each-ref-errors.sh +1 -1
index 40edf9dab53..3553f84a00c 100755
@@ -41,7 +41,7 @@ test_expect_success 'Missing objects are reported correctly' '
 	r=refs/heads/missing &&
 	echo $MISSING >.git/$r &&
 	test_when_finished "rm -f .git/$r" &&
-	echo "fatal: missing object $MISSING for $r" >missing-err &&
+	echo "fatal: $MISSING missing" >missing-err &&
 	test_must_fail git for-each-ref 2>err &&
 	test_cmp missing-err err &&
 	(
Signature · 1 line
gitgitgadget
#109 lore raw

PATCH v6 10/15 cat-file: add has_object_file() check

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Use has_object_file() in batch_one_object() to check whether the input object exists. This can help us reject the missing oid when we let cat-file --batch use ref-filter logic later.

---
 builtin/cat-file.c | 7 +++++++
 1 file changed, 7 insertions(+)
builtin/cat-file.c +7 -0
index 5ebf13359e8..9fd3c04ff20 100644
@@ -428,6 +428,13 @@ static void batch_one_object(const char *obj_name,
 		return;
 	}
 
+	if (!has_object_file(&data->oid)) {
+		printf("%s missing\n",
+		       obj_name ? obj_name : oid_to_hex(&data->oid));
+		fflush(stdout);
+		return;
+	}
+
 	batch_object_write(obj_name, scratch, opt, data);
 }
 
Signature · 1 line
gitgitgadget
#110 lore raw

PATCH v6 11/15 cat-file: change batch_objects parameter name

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because later cat-file reuses ref-filter logic that will add parameter "const struct option *options" to batch_objects(), the two synonymous parameters of "opt" and "options" may confuse readers, so change batch_options parameter of batch_objects() from "opt" to "batch".

---
 builtin/cat-file.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
builtin/cat-file.c +11 -11
index 9fd3c04ff20..cd84c39df96 100644
@@ -495,7 +495,7 @@ static int batch_unordered_packed(const struct object_id *oid,
 	return batch_unordered_object(oid, data);
 }
 
-static int batch_objects(struct batch_options *opt)
+static int batch_objects(struct batch_options *batch)
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
@@ -503,8 +503,8 @@ static int batch_objects(struct batch_options *opt)
 	int save_warning;
 	int retval = 0;
 
-	if (!opt->format)
-		opt->format = "%(objectname) %(objecttype) %(objectsize)";
+	if (!batch->format)
+		batch->format = "%(objectname) %(objecttype) %(objectsize)";
 
 	/*
 	 * Expand once with our special mark_query flag, which will prime the
@@ -513,13 +513,13 @@ static int batch_objects(struct batch_options *opt)
 	 */
 	memset(&data, 0, sizeof(data));
 	data.mark_query = 1;
-	strbuf_expand(&output, opt->format, expand_format, &data);
+	strbuf_expand(&output, batch->format, expand_format, &data);
 	data.mark_query = 0;
 	strbuf_release(&output);
-	if (opt->cmdmode)
+	if (batch->cmdmode)
 		data.split_on_whitespace = 1;
 
-	if (opt->all_objects) {
+	if (batch->all_objects) {
 		struct object_info empty = OBJECT_INFO_INIT;
 		if (!memcmp(&data.info, &empty, sizeof(empty)))
 			data.skip_object_info = 1;
@@ -529,20 +529,20 @@ static int batch_objects(struct batch_options *opt)
 	 * 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 (opt->print_contents)
+	if (batch->print_contents)
 		data.info.typep = &data.type;
 
-	if (opt->all_objects) {
+	if (batch->all_objects) {
 		struct object_cb_data cb;
 
 		if (has_promisor_remote())
 			warning("This repository uses promisor remotes. Some objects may not be loaded.");
 
-		cb.opt = opt;
+		cb.opt = batch;
 		cb.expand = &data;
 		cb.scratch = &output;
 
-		if (opt->unordered) {
+		if (batch->unordered) {
 			struct oidset seen = OIDSET_INIT;
 
 			cb.seen = &seen;
@@ -592,7 +592,7 @@ static int batch_objects(struct batch_options *opt)
 			data.rest = p;
 		}
 
-		batch_one_object(input.buf, &output, opt, &data);
+		batch_one_object(input.buf, &output, batch, &data);
 	}
 
 	strbuf_release(&input);
Signature · 1 line
gitgitgadget
#111 lore raw

PATCH v6 12/15 cat-file: reuse ref-filter logic

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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

Quoted 1 line

/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".

---
 Documentation/git-cat-file.txt |   6 +
 builtin/cat-file.c             | 244 ++++++-------------------------
 t/t1006-cat-file.sh            | 252 +++++++++++++++++++++++++++++++++
 3 files changed, 305 insertions(+), 197 deletions(-)
Documentation/git-cat-file.txt +6 -0
index 4eb0421b3fd..ef8ab952b2f 100644
@@ -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)`.
 
builtin/cat-file.c +47 -197

339 lines not on this page · raw message

t/t1006-cat-file.sh +252 -0

256 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#112 lore raw

Hi,

Quoted 20 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

Quoted 5 lines

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.

It's not clear why some atoms are rejected?

Are we going to support them in later commits? (or sometime in the future) OR
We are never going to support them. Because they make no sense to cat-file? (or whatever the reason)

Whatever is the reason, I think it's a good idea to include it in the commit message.

Signature · 1 line
Hariom
#113 lore raw

Hi,

Quoted 10 lines · Hariom verma <[email protected]> 于2021年6月28日周一 下午3:47写道:

Hi,

Quoted 6 lines

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.

Quoted 7 lines

It's not clear why some atoms are rejected?

Are we going to support them in later commits? (or sometime in the future) OR
We are never going to support them. Because they make no sense to cat-file? (or whatever the reason)

Because in "git for-each-ref"'s "family", ref_array_item is generated by filter_refs(),
which uses ref_filter_handler() to fill ref_array_item with ref's data. In "git cat-file",
we care about the object, not the ref. Therefore, ref_array_item is only filled with
{oid, rest} in batch_object_write() in cat-file.c. We cannot represent some specific
ref-related data in "git cat-file", so we cannot have some atoms in ref_filter. Yes, we probably won't support them in the future.

From an object-oriented point of view, the atom supported by "cat-file" should be a
parent class, "for-each-ref"',"branch","tag"... they have more specific object details (ref),
their supported atom should be a derived class, they can support more atoms.

Quoted 3 lines

Whatever is the reason, I think it's a good idea to include it in the commit message.

Yeah. The sentence "because our objects don't have a refname." may not correctly express the reason for rejecting these atoms. I will add more descriptions.

Quoted 2 lines

--
Hariom

Thanks.
--
ZheNing Hu

#114 lore raw

PATCH v6 13/15 cat-file: reuse err buf in batch_object_write()

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Reuse the err buffer in batch_object_write(), as the buffer scratch does. This will reduce the overhead of multiple allocations of memory of the err buffer.

---
 builtin/cat-file.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
builtin/cat-file.c +14 -8
index 5b163551fc6..dc604a9879d 100644
@@ -212,35 +212,36 @@ static void batch_write(struct batch_options *opt, const void *data, int len)
 
 static void batch_object_write(const char *obj_name,
 			       struct strbuf *scratch,
+			       struct strbuf *err,
 			       struct batch_options *opt,
 			       struct expand_data *data)
 {
 	int ret;
-	struct strbuf err = STRBUF_INIT;
 	struct ref_array_item item = { data->oid, data->rest };
 
 	strbuf_reset(scratch);
+	strbuf_reset(err);
 
-	ret = format_ref_array_item(&item, &opt->format, scratch, &err);
+	ret = format_ref_array_item(&item, &opt->format, scratch, err);
 	if (ret < 0)
-		die("%s\n", err.buf);
+		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);
+		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,
 			     struct strbuf *scratch,
+			     struct strbuf *err,
 			     struct batch_options *opt,
 			     struct expand_data *data)
 {
@@ -294,7 +295,7 @@ static void batch_one_object(const char *obj_name,
 		return;
 	}
 
-	batch_object_write(obj_name, scratch, opt, data);
+	batch_object_write(obj_name, scratch, err, opt, data);
 }
 
 struct object_cb_data {
@@ -302,13 +303,14 @@ struct object_cb_data {
 	struct expand_data *expand;
 	struct oidset *seen;
 	struct strbuf *scratch;
+	struct strbuf *err;
 };
 
 static int batch_object_cb(const struct object_id *oid, void *vdata)
 {
 	struct object_cb_data *data = vdata;
 	oidcpy(&data->expand->oid, oid);
-	batch_object_write(NULL, data->scratch, data->opt, data->expand);
+	batch_object_write(NULL, data->scratch, data->err, data->opt, data->expand);
 	return 0;
 }
 
@@ -364,6 +366,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 {
 	struct strbuf input = STRBUF_INIT;
 	struct strbuf output = STRBUF_INIT;
+	struct strbuf err = STRBUF_INIT;
 	struct strbuf format = STRBUF_INIT;
 	struct expand_data data;
 	int save_warning;
@@ -392,6 +395,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 		cb.opt = batch;
 		cb.expand = &data;
 		cb.scratch = &output;
+		cb.err = &err;
 
 		if (batch->unordered) {
 			struct oidset seen = OIDSET_INIT;
@@ -416,6 +420,7 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 
 		strbuf_release(&format);
 		strbuf_release(&output);
+		strbuf_release(&err);
 		return 0;
 	}
 
@@ -444,12 +449,13 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 			data.rest = p;
 		}
 
-		batch_one_object(input.buf, &output, batch, &data);
+		batch_one_object(input.buf, &output, &err, batch, &data);
 	}
 
 	strbuf_release(&format);
 	strbuf_release(&input);
 	strbuf_release(&output);
+	strbuf_release(&err);
 	warn_on_object_refname_ambiguity = save_warning;
 	return retval;
 }
Signature · 1 line
gitgitgadget
#115 lore raw

PATCH v6 14/15 cat-file: re-implement --textconv, --filters options

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

After cat-file reuses the ref-filter logic, we re-implement the functions of --textconv and --filters options.

Add members use_textconv and use_filters in struct ref_format, and use global variables use_filters and use_textconv in ref-filter.c, so that we can filter the content of the object in get_object(). Use actual_oi to record the real expand_data:
it may point to the original oi or the act_oi processed by textconv_object() or convert_to_working_tree(). grab_values() will grab the contents of actual_oi and grab_common_values() to grab the contents of origin oi, this ensures that %(objectsize) still uses the size of the unfiltered data.

In get_object(), we made an optimization: Firstly, get the size and type of the object instead of directly getting the object data. If using --textconv, after successfully obtaining the filtered object data, an extra oid_object_info_extended() will be skipped, which can reduce the cost of object data copy; If using --filter, the data of the object first will be getted first, and then convert_to_working_tree() will be used to get the filtered object data.

---
 builtin/cat-file.c |  6 +++++
 ref-filter.c       | 59 ++++++++++++++++++++++++++++++++++++++++++++--
 ref-filter.h       |  2 ++
 3 files changed, 65 insertions(+), 2 deletions(-)
builtin/cat-file.c +6 -0
index dc604a9879d..710085b7d72 100644
@@ -380,6 +380,12 @@ static int batch_objects(struct batch_options *batch, const struct option *optio
 	if (batch->print_contents)
 		strbuf_addstr(&format, "\n%(raw)");
 	batch->format.format = format.buf;
+
+	if (batch->cmdmode == 'c')
+		batch->format.use_textconv = 1;
+	else if (batch->cmdmode == 'w')
+		batch->format.use_filters = 1;
+
 	if (verify_ref_format(&batch->format))
 		usage_with_options(cat_file_usage, options);
 
ref-filter.c +57 -2

101 lines not on this page · raw message

ref-filter.h +2 -0
index 053980a6a42..497e3e93632 100644
@@ -80,6 +80,8 @@ struct ref_format {
 	const char *rest;
 	int cat_file_mode;
 	int quote_style;
+	int use_textconv;
+	int use_filters;
 	int use_rest;
 	int use_color;
 
Signature · 1 line
gitgitgadget
#116 lore raw

PATCH v6 15/15 ref-filter: remove grab_oid() function

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Because "atom_type == ATOM_OBJECTNAME" implies the condition of starts_with(name, "objectname"), "atom_type == ATOM_TREE" implies the condition of starts_with(name, "tree"), so the check for starts_with(name, field) in grab_oid() is redundant.

So Remove the grab_oid() from ref-filter, to reduce repeated check.

---
 ref-filter.c | 26 +++++++++-----------------
 1 file changed, 9 insertions(+), 17 deletions(-)
ref-filter.c +9 -17
index 427552b8108..55bff4ed9e7 100644
@@ -1071,16 +1071,6 @@ static const char *do_grab_oid(const char *field, const struct object_id *oid,
 	}
 }
 
-static int grab_oid(const char *name, const char *field, const struct object_id *oid,
-		    struct atom_value *v, struct used_atom *atom)
-{
-	if (starts_with(name, field)) {
-		v->s = xstrdup(do_grab_oid(field, oid, atom));
-		return 1;
-	}
-	return 0;
-}
-
 /* See grab_values */
 static void grab_common_values(struct atom_value *val, int deref, struct expand_data *oi)
 {
@@ -1106,8 +1096,9 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
 			}
 		} else if (atom_type == ATOM_DELTABASE)
 			v->s = xstrdup(oid_to_hex(&oi->delta_base_oid));
-		else if (atom_type == ATOM_OBJECTNAME && deref)
-			grab_oid(name, "objectname", &oi->oid, v, &used_atom[i]);
+		else if (atom_type == ATOM_OBJECTNAME && deref) {
+			v->s = xstrdup(do_grab_oid("objectname", &oi->oid, &used_atom[i]));
+		}
 	}
 }
 
@@ -1148,9 +1139,10 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
 			continue;
 		if (deref)
 			name++;
-		if (atom_type == ATOM_TREE &&
-		    grab_oid(name, "tree", get_commit_tree_oid(commit), v, &used_atom[i]))
+		if (atom_type == ATOM_TREE) {
+			v->s = xstrdup(do_grab_oid("tree", get_commit_tree_oid(commit), &used_atom[i]));
 			continue;
+		}
 		if (atom_type == ATOM_NUMPARENT) {
 			v->value = commit_list_count(commit->parents);
 			v->s = xstrfmt("%lu", (unsigned long)v->value);
@@ -1971,9 +1963,9 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 				v->s = xstrdup(buf + 1);
 			}
 			continue;
-		} else if (!deref && atom_type == ATOM_OBJECTNAME &&
-			   grab_oid(name, "objectname", &ref->objectname, v, atom)) {
-				continue;
+		} else if (!deref && atom_type == ATOM_OBJECTNAME) {
+			   v->s = xstrdup(do_grab_oid("objectname", &ref->objectname, atom));
+			   continue;
 		} else if (atom_type == ATOM_HEAD) {
 			if (atom->u.head && !strcmp(ref->refname, atom->u.head))
 				v->s = xstrdup("*");
Signature · 1 line
gitgitgadget
#118 lore raw
Quoted 8 lines · Junio C Hamano <[email protected]> 于2021年7月1日周四 上午6:04写道:
Quoted 1 line · ZheNing Hu via GitGitGadget

This patch series make cat-file reuse ref-filter logic.

Unfortunately this seems to interact with your own zh/cat-file-batch-fix rather badly.

Well, it's because I didn't base this patch on it. That should be easy to achieve.

By the way, I think patches before "[GSOC] ref-filter: add %(rest) atom" should belong to "zh/ref-filter-raw-data", and patches after that should belong to "zh/cat-file-batch-refactor".

Thanks.
--
ZheNing Hu

#119 lore raw
Quoted 4 lines · ZheNing Hu
Quoted 3 lines · Junio C Hamano <[email protected]> 于2021年7月1日周四 上午6:04写道:

"ZheNing Hu via GitGitGadget" <[email protected]> writes:

Quoted 8 lines
Quoted 5 lines
Quoted 1 line

This patch series make cat-file reuse ref-filter logic.

Unfortunately this seems to interact with your own zh/cat-file-batch-fix rather badly.

Well, it's because I didn't base this patch on it. That should be easy to achieve.

It is preferrable for contributors try merging their individual topics with the rest of 'seen' to see if there are potential conflicts (either textual or semantic) before sending their topics out. Not all topics need to build on top of other topics (in fact, the fewer inter-dependencies they have, the better), but in this case, I think it makes sense to build one on top of the other.

#120 lore raw

Hi, Junio,

Quoted 16 lines · Junio C Hamano <[email protected]> 于2021年7月1日周四 下午10:17写道:
Quoted 4 lines · ZheNing Hu
Quoted 3 lines · Junio C Hamano <[email protected]> 于2021年7月1日周四 上午6:04写道:

"ZheNing Hu via GitGitGadget" <[email protected]> writes:

Quoted 8 lines
Quoted 5 lines
Quoted 1 line

This patch series make cat-file reuse ref-filter logic.

Unfortunately this seems to interact with your own zh/cat-file-batch-fix rather badly.

Well, it's because I didn't base this patch on it. That should be easy to achieve.

Quoted 7 lines

It is preferrable for contributors try merging their individual topics with the rest of 'seen' to see if there are potential conflicts (either textual or semantic) before sending their topics out. Not all topics need to build on top of other topics (in fact, the fewer inter-dependencies they have, the better), but in this case, I think it makes sense to build one on top of the other.

I have a "rebase" trouble:

My new feature branch "cat-file-batch-refactor-rebase-version" should base on zh/cat-file-batch-fix and zh/ref-filter-atom-type, so last time I choice (bb9a3a8f77 Merge branch 'zh/cat-file-batch-fix' into jch) as the patch base.

But github only allow me base the patch on a branch, so I choice "gitgitgadget:seen"
as my github PR base. It causes that some merge commit include in it. [1]

So In order to prevent these "merge" commits from being sent, the GGG mechanism is modified to reject their merge commits.

Now I can't choice a good branch as my patch base... Have any ideas?

Thanks.

[1]: https://github.com/gitgitgadget/git/pull/989

--
ZheNing Hu