diff options
author | Kousik Sanagavarapu <five231003@gmail.com> | 2023-07-23 19:19:59 +0300 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2023-07-24 20:42:29 +0300 |
commit | f5d18f8c0ef9cc3e62420268c2e72d1fd46b940c (patch) | |
tree | a8dd3a165f6cf3a8b0e88004b04d424c178f4fcc | |
parent | f46094a5e6b80098786b4e1448be032dfbdf3f43 (diff) |
ref-filter: add new "describe" atom
Duplicate the logic of %(describe) and friends from pretty to
ref-filter. In the future, this change helps in unifying both the
formats as ref-filter will be able to do everything that pretty is doing
and we can have a single interface.
The new atom "describe" and its friends are equivalent to the existing
pretty formats with the same name.
Helped-by: Junio C Hamano <gitster@pobox.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Hariom Verma <hariom18599@gmail.com>
Signed-off-by: Kousik Sanagavarapu <five231003@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r-- | Documentation/git-for-each-ref.txt | 23 | ||||
-rw-r--r-- | ref-filter.c | 125 | ||||
-rwxr-xr-x | t/t6300-for-each-ref.sh | 138 |
3 files changed, 286 insertions, 0 deletions
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 2e0318770b..5146864300 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -258,6 +258,29 @@ ahead-behind:<committish>:: commits ahead and behind, respectively, when comparing the output ref to the `<committish>` specified in the format. +describe[:options]:: + A human-readable name, like linkgit:git-describe[1]; + empty string for undescribable commits. The `describe` string may + be followed by a colon and one or more comma-separated options. ++ +-- +tags=<bool-value>;; + Instead of only considering annotated tags, consider + lightweight tags as well; see the corresponding option in + linkgit:git-describe[1] for details. +abbrev=<number>;; + Use at least <number> hexadecimal digits; see the corresponding + option in linkgit:git-describe[1] for details. +match=<pattern>;; + Only consider tags matching the given `glob(7)` pattern, + excluding the "refs/tags/" prefix; see the corresponding option + in linkgit:git-describe[1] for details. +exclude=<pattern>;; + Do not consider tags matching the given `glob(7)` pattern, + excluding the "refs/tags/" prefix; see the corresponding option + in linkgit:git-describe[1] for details. +-- + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/ref-filter.c b/ref-filter.c index 66b8bc586d..8ce72733c8 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -5,6 +5,7 @@ #include "gpg-interface.h" #include "hex.h" #include "parse-options.h" +#include "run-command.h" #include "refs.h" #include "wildmatch.h" #include "object-name.h" @@ -146,6 +147,7 @@ enum atom_type { ATOM_TAGGERDATE, ATOM_CREATOR, ATOM_CREATORDATE, + ATOM_DESCRIBE, ATOM_SUBJECT, ATOM_BODY, ATOM_TRAILERS, @@ -220,6 +222,7 @@ static struct used_atom { enum { S_BARE, S_GRADE, S_SIGNER, S_KEY, S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option; } signature; + const char **describe_args; struct refname_atom refname; char *head; } u; @@ -600,6 +603,87 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato return 0; } +static int describe_atom_option_parser(struct strvec *args, const char **arg, + struct strbuf *err) +{ + const char *argval; + size_t arglen = 0; + int optval = 0; + + if (match_atom_bool_arg(*arg, "tags", arg, &optval)) { + if (!optval) + strvec_push(args, "--no-tags"); + else + strvec_push(args, "--tags"); + return 1; + } + + if (match_atom_arg_value(*arg, "abbrev", arg, &argval, &arglen)) { + char *endptr; + + if (!arglen) + return strbuf_addf_ret(err, -1, + _("argument expected for %s"), + "describe:abbrev"); + if (strtol(argval, &endptr, 10) < 0) + return strbuf_addf_ret(err, -1, + _("positive value expected %s=%s"), + "describe:abbrev", argval); + if (endptr - argval != arglen) + return strbuf_addf_ret(err, -1, + _("cannot fully parse %s=%s"), + "describe:abbrev", argval); + + strvec_pushf(args, "--abbrev=%.*s", (int)arglen, argval); + return 1; + } + + if (match_atom_arg_value(*arg, "match", arg, &argval, &arglen)) { + if (!arglen) + return strbuf_addf_ret(err, -1, + _("value expected %s="), + "describe:match"); + + strvec_pushf(args, "--match=%.*s", (int)arglen, argval); + return 1; + } + + if (match_atom_arg_value(*arg, "exclude", arg, &argval, &arglen)) { + if (!arglen) + return strbuf_addf_ret(err, -1, + _("value expected %s="), + "describe:exclude"); + + strvec_pushf(args, "--exclude=%.*s", (int)arglen, argval); + return 1; + } + + return 0; +} + +static int describe_atom_parser(struct ref_format *format UNUSED, + struct used_atom *atom, + const char *arg, struct strbuf *err) +{ + struct strvec args = STRVEC_INIT; + + for (;;) { + int found = 0; + const char *bad_arg = arg; + + if (!arg || !*arg) + break; + + found = describe_atom_option_parser(&args, &arg, err); + if (found < 0) + return found; + if (!found) + return err_bad_arg(err, "describe", bad_arg); + } + atom->u.describe_args = strvec_detach(&args); + return 0; +} + static int raw_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom, const char *arg, struct strbuf *err) @@ -802,6 +886,7 @@ static struct { [ATOM_TAGGERDATE] = { "taggerdate", SOURCE_OBJ, FIELD_TIME }, [ATOM_CREATOR] = { "creator", SOURCE_OBJ }, [ATOM_CREATORDATE] = { "creatordate", SOURCE_OBJ, FIELD_TIME }, + [ATOM_DESCRIBE] = { "describe", SOURCE_OBJ, FIELD_STR, describe_atom_parser }, [ATOM_SUBJECT] = { "subject", SOURCE_OBJ, FIELD_STR, subject_atom_parser }, [ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser }, [ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser }, @@ -1708,6 +1793,44 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size } } +static void grab_describe_values(struct atom_value *val, int deref, + struct object *obj) +{ + struct commit *commit = (struct commit *)obj; + int i; + + for (i = 0; i < used_atom_cnt; i++) { + struct used_atom *atom = &used_atom[i]; + enum atom_type type = atom->atom_type; + const char *name = atom->name; + struct atom_value *v = &val[i]; + + struct child_process cmd = CHILD_PROCESS_INIT; + struct strbuf out = STRBUF_INIT; + struct strbuf err = STRBUF_INIT; + + if (type != ATOM_DESCRIBE) + continue; + + if (!!deref != (*name == '*')) + continue; + + cmd.git_cmd = 1; + strvec_push(&cmd.args, "describe"); + strvec_pushv(&cmd.args, atom->u.describe_args); + strvec_push(&cmd.args, oid_to_hex(&commit->object.oid)); + if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) { + error(_("failed to run 'describe'")); + v->s = xstrdup(""); + continue; + } + strbuf_rtrim(&out); + v->s = strbuf_detach(&out, NULL); + + strbuf_release(&err); + } +} + /* See grab_values */ static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data) { @@ -1817,6 +1940,7 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s grab_tag_values(val, deref, obj); grab_sub_body_contents(val, deref, data); grab_person("tagger", val, deref, buf); + grab_describe_values(val, deref, obj); break; case OBJ_COMMIT: grab_commit_values(val, deref, obj); @@ -1824,6 +1948,7 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s grab_person("author", val, deref, buf); grab_person("committer", val, deref, buf); grab_signature(val, deref, obj); + grab_describe_values(val, deref, obj); break; case OBJ_TREE: /* grab_tree_values(val, deref, obj, buf, sz); */ diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 6e6ec852b5..2325f25901 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -562,6 +562,144 @@ test_expect_success 'color.ui=always does not override tty check' ' test_cmp expected.bare actual ' +test_expect_success 'setup for describe atom tests' ' + git init -b master describe-repo && + ( + cd describe-repo && + + test_commit --no-tag one && + git tag tagone && + + test_commit --no-tag two && + git tag -a -m "tag two" tagtwo + ) +' + +test_expect_success 'describe atom vs git describe' ' + ( + cd describe-repo && + + git for-each-ref --format="%(objectname)" \ + refs/tags/ >obj && + while read hash + do + if desc=$(git describe $hash) + then + : >expect-contains-good + else + : >expect-contains-bad + fi && + echo "$hash $desc" || return 1 + done <obj >expect && + test_path_exists expect-contains-good && + test_path_exists expect-contains-bad && + + git for-each-ref --format="%(objectname) %(describe)" \ + refs/tags/ >actual 2>err && + test_cmp expect actual && + test_must_be_empty err + ) +' + +test_expect_success 'describe:tags vs describe --tags' ' + ( + cd describe-repo && + git describe --tags >expect && + git for-each-ref --format="%(describe:tags)" \ + refs/heads/master >actual && + test_cmp expect actual + ) +' + +test_expect_success 'describe:abbrev=... vs describe --abbrev=...' ' + ( + cd describe-repo && + + # Case 1: We have commits between HEAD and the most + # recent tag reachable from it + test_commit --no-tag file && + git describe --abbrev=14 >expect && + git for-each-ref --format="%(describe:abbrev=14)" \ + refs/heads/master >actual && + test_cmp expect actual && + + # Make sure the hash used is atleast 14 digits long + sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart && + test 15 -le $(wc -c <hexpart) && + + # Case 2: We have a tag at HEAD, describe directly gives + # the name of the tag + git tag -a -m tagged tagname && + git describe --abbrev=14 >expect && + git for-each-ref --format="%(describe:abbrev=14)" \ + refs/heads/master >actual && + test_cmp expect actual && + test tagname = $(cat actual) + ) +' + +test_expect_success 'describe:match=... vs describe --match ...' ' + ( + cd describe-repo && + git tag -a -m "tag foo" tag-foo && + git describe --match "*-foo" >expect && + git for-each-ref --format="%(describe:match="*-foo")" \ + refs/heads/master >actual && + test_cmp expect actual + ) +' + +test_expect_success 'describe:exclude:... vs describe --exclude ...' ' + ( + cd describe-repo && + git tag -a -m "tag bar" tag-bar && + git describe --exclude "*-bar" >expect && + git for-each-ref --format="%(describe:exclude="*-bar")" \ + refs/heads/master >actual && + test_cmp expect actual + ) +' + +test_expect_success 'deref with describe atom' ' + ( + cd describe-repo && + cat >expect <<-\EOF && + + tagname + tagname + tagname + + tagtwo + EOF + git for-each-ref --format="%(*describe)" >actual && + test_cmp expect actual + ) +' + +test_expect_success 'err on bad describe atom arg' ' + ( + cd describe-repo && + + # The bad arg is the only arg passed to describe atom + cat >expect <<-\EOF && + fatal: unrecognized %(describe) argument: baz + EOF + test_must_fail git for-each-ref --format="%(describe:baz)" \ + refs/heads/master 2>actual && + test_cmp expect actual && + + # The bad arg is in the middle of the option string + # passed to the describe atom + cat >expect <<-\EOF && + fatal: unrecognized %(describe) argument: qux=1,abbrev=14 + EOF + test_must_fail git for-each-ref \ + --format="%(describe:tags,qux=1,abbrev=14)" \ + ref/heads/master 2>actual && + test_cmp expect actual + ) +' + cat >expected <<\EOF heads/main tags/main |