From: "Victoria Dye via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Victoria Dye <vdye@github.com>, Victoria Dye <vdye@github.com>
Subject: [PATCH 8/9] for-each-ref: add option to fully dereference tags
Date: Tue, 07 Nov 2023 01:26:00 +0000 [thread overview]
Message-ID: <352b5c42ac39d5d2646a1b6d47d6d707637db539.1699320362.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1609.git.1699320361.gitgitgadget@gmail.com>
From: Victoria Dye <vdye@github.com>
Add a boolean flag '--full-deref' that, when enabled, fills '%(*fieldname)'
format fields using the fully peeled target of tag objects, rather than the
immediate target.
In other builtins ('rev-parse', 'show-ref'), "dereferencing" tags typically
means peeling them down to their non-tag target. Unlike these commands,
'for-each-ref' dereferences only one "level" of tags in '*' format fields
(like "%(*objectname)"). For most annotated tags, one level of dereferencing
is enough, since most tags point to commits or trees. However, nested tags
(annotated tags whose target is another annotated tag) dereferenced once
will point to their target tag, different a full peel to e.g. a commit.
Currently, if a user wants to filter & format refs and include information
about the fully dereferenced tag, they can do so with something like
'cat-file --batch-check':
git for-each-ref --format="%(objectname)^{} %(refname)" <pattern> |
git cat-file --batch-check="%(objectname) %(rest)"
But the combination of commands is inefficient. So, to improve the
efficiency of this use case, add a '--full-deref' option that causes
'for-each-ref' to fully dereference tags when formatting with '*' fields.
Signed-off-by: Victoria Dye <vdye@github.com>
---
Documentation/git-for-each-ref.txt | 9 ++++++++
builtin/for-each-ref.c | 2 ++
ref-filter.c | 26 ++++++++++++++---------
ref-filter.h | 1 +
t/t6300-for-each-ref.sh | 34 ++++++++++++++++++++++++++++++
5 files changed, 62 insertions(+), 10 deletions(-)
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 407f624fbaa..2714a87088e 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -11,6 +11,7 @@ SYNOPSIS
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
[(--sort=<key>)...] [--format=<format>]
[ --stdin | <pattern>... ]
+ [--full-deref]
[--points-at=<object>]
[--merged[=<object>]] [--no-merged[=<object>]]
[--contains[=<object>]] [--no-contains[=<object>]]
@@ -77,6 +78,14 @@ OPTIONS
the specified host language. This is meant to produce
a scriptlet that can directly be `eval`ed.
+--full-deref::
+ Populate dereferenced format fields (indicated with an asterisk (`*`)
+ prefix before the fieldname) with information about the fully-peeled
+ target object of a tag ref, rather than its immediate target object.
+ This only affects the output for nested annotated tags, where the tag's
+ immediate target is another tag but its fully-peeled target is another
+ object type (e.g. a commit).
+
--points-at=<object>::
Only list refs which points at the given object.
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 1c19cd5bd34..7a2127a3bc4 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -43,6 +43,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT__COLOR(&format.use_color, N_("respect format colors")),
+ OPT_BOOL(0, "full-deref", &format.full_deref,
+ N_("fully dereference tags to populate '*' format fields")),
OPT_REF_FILTER_EXCLUDE(&filter),
OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at,
diff --git a/ref-filter.c b/ref-filter.c
index 384cf1595ff..a66ac7921b1 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -237,7 +237,14 @@ static struct used_atom {
char *head;
} u;
} *used_atom;
-static int used_atom_cnt, need_tagged, need_symref;
+static int used_atom_cnt, need_symref;
+
+enum tag_dereference_mode {
+ NO_DEREF = 0,
+ DEREF_ONE,
+ DEREF_ALL
+};
+static enum tag_dereference_mode need_tagged;
/*
* Expand string, append it to strbuf *sb, then return error code ret.
@@ -1066,8 +1073,8 @@ static int parse_ref_filter_atom(struct ref_format *format,
memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
if (valid_atom[i].parser && valid_atom[i].parser(format, &used_atom[at], arg, err))
return -1;
- if (*atom == '*')
- need_tagged = 1;
+ if (*atom == '*' && !need_tagged)
+ need_tagged = format->full_deref ? DEREF_ALL : DEREF_ONE;
if (i == ATOM_SYMREF)
need_symref = 1;
return at;
@@ -2511,14 +2518,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
* If it is a tag object, see if we use a value that derefs
* the object, and if we do grab the object it refers to.
*/
- oi_deref.oid = *get_tagged_oid((struct tag *)obj);
+ if (need_tagged == DEREF_ALL) {
+ if (peel_iterated_oid(&obj->oid, &oi_deref.oid))
+ die("bad tag");
+ } else {
+ oi_deref.oid = *get_tagged_oid((struct tag *)obj);
+ }
- /*
- * NEEDSWORK: This derefs tag only once, which
- * is good to deal with chains of trust, but
- * is not consistent with what deref_tag() does
- * which peels the onion to the core.
- */
return get_object(ref, 1, &obj, &oi_deref, err);
}
diff --git a/ref-filter.h b/ref-filter.h
index 0ce5af58ab3..0caa39ecee5 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -92,6 +92,7 @@ struct ref_format {
const char *rest;
int quote_style;
int use_color;
+ int full_deref;
/* Internal state to ref-filter */
int need_color_reset_at_eol;
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 0613e5e3623..3c2af785cdb 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -1839,6 +1839,40 @@ test_expect_success 'git for-each-ref with non-existing refs' '
test_must_be_empty actual
'
+test_expect_success 'git for-each-ref with nested tags' '
+ git tag -am "Normal tag" nested/base HEAD &&
+ git tag -am "Nested tag" nested/nest1 refs/tags/nested/base &&
+ git tag -am "Double nested tag" nested/nest2 refs/tags/nested/nest1 &&
+
+ head_oid="$(git rev-parse HEAD)" &&
+ base_tag_oid="$(git rev-parse refs/tags/nested/base)" &&
+ nest1_tag_oid="$(git rev-parse refs/tags/nested/nest1)" &&
+ nest2_tag_oid="$(git rev-parse refs/tags/nested/nest2)" &&
+
+ # Without full dereference
+ cat >expect <<-EOF &&
+ refs/tags/nested/base $base_tag_oid tag $head_oid commit
+ refs/tags/nested/nest1 $nest1_tag_oid tag $base_tag_oid tag
+ refs/tags/nested/nest2 $nest2_tag_oid tag $nest1_tag_oid tag
+ EOF
+
+ git for-each-ref --format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \
+ refs/tags/nested/ >actual &&
+ test_cmp expect actual &&
+
+ # With full dereference
+ cat >expect <<-EOF &&
+ refs/tags/nested/base $base_tag_oid tag $head_oid commit
+ refs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit
+ refs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit
+ EOF
+
+ git for-each-ref --full-deref \
+ --format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \
+ refs/tags/nested/ >actual &&
+ test_cmp expect actual
+'
+
GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
--
gitgitgadget
next prev parent reply other threads:[~2023-11-07 1:26 UTC|newest]
Thread overview: 49+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-07 1:25 [PATCH 0/9] for-each-ref optimizations & usability improvements Victoria Dye via GitGitGadget
2023-11-07 1:25 ` [PATCH 1/9] ref-filter.c: really don't sort when using --no-sort Victoria Dye via GitGitGadget
2023-11-07 10:49 ` Patrick Steinhardt
2023-11-07 18:13 ` Victoria Dye
2023-11-07 1:25 ` [PATCH 2/9] for-each-ref: clarify interaction of --omit-empty & --count Victoria Dye via GitGitGadget
2023-11-07 19:23 ` Øystein Walle
2023-11-07 19:30 ` Victoria Dye
2023-11-08 7:53 ` Øystein Walle
2023-11-08 10:00 ` Kristoffer Haugsbakk
2023-11-07 1:25 ` [PATCH 3/9] ref-filter.h: add max_count and omit_empty to ref_format Victoria Dye via GitGitGadget
2023-11-07 1:25 ` [PATCH 4/9] ref-filter.h: move contains caches into filter Victoria Dye via GitGitGadget
2023-11-07 10:49 ` Patrick Steinhardt
2023-11-07 1:25 ` [PATCH 5/9] ref-filter.h: add functions for filter/format & format-only Victoria Dye via GitGitGadget
2023-11-07 1:25 ` [PATCH 6/9] ref-filter.c: refactor to create common helper functions Victoria Dye via GitGitGadget
2023-11-07 10:49 ` Patrick Steinhardt
2023-11-07 18:41 ` Victoria Dye
2023-11-07 1:25 ` [PATCH 7/9] ref-filter.c: filter & format refs in the same callback Victoria Dye via GitGitGadget
2023-11-07 10:49 ` Patrick Steinhardt
2023-11-07 19:45 ` Victoria Dye
2023-11-07 1:26 ` Victoria Dye via GitGitGadget [this message]
2023-11-07 10:50 ` [PATCH 8/9] for-each-ref: add option to fully dereference tags Patrick Steinhardt
2023-11-08 1:13 ` Victoria Dye
2023-11-08 3:14 ` Junio C Hamano
2023-11-08 7:19 ` Patrick Steinhardt
2023-11-08 18:02 ` Victoria Dye
2023-11-09 1:22 ` Junio C Hamano
2023-11-09 1:23 ` Junio C Hamano
2023-11-09 1:32 ` Junio C Hamano
2023-11-07 1:26 ` [PATCH 9/9] t/perf: add perf tests for for-each-ref Victoria Dye via GitGitGadget
2023-11-07 2:36 ` [PATCH 0/9] for-each-ref optimizations & usability improvements Junio C Hamano
2023-11-07 2:48 ` Victoria Dye
2023-11-07 3:04 ` Junio C Hamano
2023-11-07 10:49 ` Patrick Steinhardt
2023-11-08 1:31 ` Victoria Dye
2023-11-14 19:53 ` [PATCH v2 00/10] " Victoria Dye via GitGitGadget
2023-11-14 19:53 ` [PATCH v2 01/10] ref-filter.c: really don't sort when using --no-sort Victoria Dye via GitGitGadget
2023-11-16 5:29 ` Junio C Hamano
2023-11-14 19:53 ` [PATCH v2 02/10] ref-filter.h: add max_count and omit_empty to ref_format Victoria Dye via GitGitGadget
2023-11-16 12:06 ` Øystein Walle
2023-11-14 19:53 ` [PATCH v2 03/10] ref-filter.h: move contains caches into filter Victoria Dye via GitGitGadget
2023-11-14 19:53 ` [PATCH v2 04/10] ref-filter.h: add functions for filter/format & format-only Victoria Dye via GitGitGadget
2023-11-16 5:39 ` Junio C Hamano
2023-11-14 19:53 ` [PATCH v2 05/10] ref-filter.c: rename 'ref_filter_handler()' to 'filter_one()' Victoria Dye via GitGitGadget
2023-11-14 19:53 ` [PATCH v2 06/10] ref-filter.c: refactor to create common helper functions Victoria Dye via GitGitGadget
2023-11-14 19:53 ` [PATCH v2 07/10] ref-filter.c: filter & format refs in the same callback Victoria Dye via GitGitGadget
2023-11-14 19:53 ` [PATCH v2 08/10] for-each-ref: clean up documentation of --format Victoria Dye via GitGitGadget
2023-11-14 19:53 ` [PATCH v2 09/10] ref-filter.c: use peeled tag for '*' format fields Victoria Dye via GitGitGadget
2023-11-16 5:48 ` Junio C Hamano
2023-11-14 19:53 ` [PATCH v2 10/10] t/perf: add perf tests for for-each-ref Victoria Dye via GitGitGadget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=352b5c42ac39d5d2646a1b6d47d6d707637db539.1699320362.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=vdye@github.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).