Skip to content

Commit 70e5c5d

Browse files
committed
Merge branch 'ks/ref-filter-describe'
"git branch --list --format=<format>" and friends are taught a new "%(describe)" placeholder. * ks/ref-filter-describe: ref-filter: add new "describe" atom ref-filter: add multiple-option parsing functions
2 parents 8bfb359 + f5d18f8 commit 70e5c5d

File tree

3 files changed

+391
-0
lines changed

3 files changed

+391
-0
lines changed

Documentation/git-for-each-ref.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,29 @@ ahead-behind:<committish>::
264264
commits ahead and behind, respectively, when comparing the output
265265
ref to the `<committish>` specified in the format.
266266

267+
describe[:options]::
268+
A human-readable name, like linkgit:git-describe[1];
269+
empty string for undescribable commits. The `describe` string may
270+
be followed by a colon and one or more comma-separated options.
271+
+
272+
--
273+
tags=<bool-value>;;
274+
Instead of only considering annotated tags, consider
275+
lightweight tags as well; see the corresponding option in
276+
linkgit:git-describe[1] for details.
277+
abbrev=<number>;;
278+
Use at least <number> hexadecimal digits; see the corresponding
279+
option in linkgit:git-describe[1] for details.
280+
match=<pattern>;;
281+
Only consider tags matching the given `glob(7)` pattern,
282+
excluding the "refs/tags/" prefix; see the corresponding option
283+
in linkgit:git-describe[1] for details.
284+
exclude=<pattern>;;
285+
Do not consider tags matching the given `glob(7)` pattern,
286+
excluding the "refs/tags/" prefix; see the corresponding option
287+
in linkgit:git-describe[1] for details.
288+
--
289+
267290
In addition to the above, for commit and tag objects, the header
268291
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
269292
be used to specify the value in the header field.

ref-filter.c

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#include "git-compat-util.h"
22
#include "environment.h"
33
#include "gettext.h"
4+
#include "config.h"
45
#include "gpg-interface.h"
56
#include "hex.h"
67
#include "parse-options.h"
8+
#include "run-command.h"
79
#include "refs.h"
810
#include "wildmatch.h"
911
#include "object-name.h"
@@ -145,6 +147,7 @@ enum atom_type {
145147
ATOM_TAGGERDATE,
146148
ATOM_CREATOR,
147149
ATOM_CREATORDATE,
150+
ATOM_DESCRIBE,
148151
ATOM_SUBJECT,
149152
ATOM_BODY,
150153
ATOM_TRAILERS,
@@ -219,6 +222,7 @@ static struct used_atom {
219222
enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
220223
S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option;
221224
} signature;
225+
const char **describe_args;
222226
struct refname_atom refname;
223227
char *head;
224228
} u;
@@ -255,6 +259,110 @@ static int err_bad_arg(struct strbuf *sb, const char *name, const char *arg)
255259
return -1;
256260
}
257261

262+
/*
263+
* Parse option of name "candidate" in the option string "to_parse" of
264+
* the form
265+
*
266+
* "candidate1[=val1],candidate2[=val2],candidate3[=val3],..."
267+
*
268+
* The remaining part of "to_parse" is stored in "end" (if we are
269+
* parsing the last candidate, then this is NULL) and the value of
270+
* the candidate is stored in "valuestart" and its length in "valuelen",
271+
* that is the portion after "=". Since it is possible for a "candidate"
272+
* to not have a value, in such cases, "valuestart" is set to point to
273+
* NULL and "valuelen" to 0.
274+
*
275+
* The function returns 1 on success. It returns 0 if we don't find
276+
* "candidate" in "to_parse" or we find "candidate" but it is followed
277+
* by more chars (for example, "candidatefoo"), that is, we don't find
278+
* an exact match.
279+
*
280+
* This function only does the above for one "candidate" at a time. So
281+
* it has to be called each time trying to parse a "candidate" in the
282+
* option string "to_parse".
283+
*/
284+
static int match_atom_arg_value(const char *to_parse, const char *candidate,
285+
const char **end, const char **valuestart,
286+
size_t *valuelen)
287+
{
288+
const char *atom;
289+
290+
if (!skip_prefix(to_parse, candidate, &atom))
291+
return 0; /* definitely not "candidate" */
292+
293+
if (*atom == '=') {
294+
/* we just saw "candidate=" */
295+
*valuestart = atom + 1;
296+
atom = strchrnul(*valuestart, ',');
297+
*valuelen = atom - *valuestart;
298+
} else if (*atom != ',' && *atom != '\0') {
299+
/* key begins with "candidate" but has more chars */
300+
return 0;
301+
} else {
302+
/* just "candidate" without "=val" */
303+
*valuestart = NULL;
304+
*valuelen = 0;
305+
}
306+
307+
/* atom points at either the ',' or NUL after this key[=val] */
308+
if (*atom == ',')
309+
atom++;
310+
else if (*atom)
311+
BUG("Why is *atom not NULL yet?");
312+
313+
*end = atom;
314+
return 1;
315+
}
316+
317+
/*
318+
* Parse boolean option of name "candidate" in the option list "to_parse"
319+
* of the form
320+
*
321+
* "candidate1[=bool1],candidate2[=bool2],candidate3[=bool3],..."
322+
*
323+
* The remaining part of "to_parse" is stored in "end" (if we are parsing
324+
* the last candidate, then this is NULL) and the value (if given) is
325+
* parsed and stored in "val", so "val" always points to either 0 or 1.
326+
* If the value is not given, then "val" is set to point to 1.
327+
*
328+
* The boolean value is parsed using "git_parse_maybe_bool()", so the
329+
* accepted values are
330+
*
331+
* to set true - "1", "yes", "true"
332+
* to set false - "0", "no", "false"
333+
*
334+
* This function returns 1 on success. It returns 0 when we don't find
335+
* an exact match for "candidate" or when the boolean value given is
336+
* not valid.
337+
*/
338+
static int match_atom_bool_arg(const char *to_parse, const char *candidate,
339+
const char **end, int *val)
340+
{
341+
const char *argval;
342+
char *strval;
343+
size_t arglen;
344+
int v;
345+
346+
if (!match_atom_arg_value(to_parse, candidate, end, &argval, &arglen))
347+
return 0;
348+
349+
if (!argval) {
350+
*val = 1;
351+
return 1;
352+
}
353+
354+
strval = xstrndup(argval, arglen);
355+
v = git_parse_maybe_bool(strval);
356+
free(strval);
357+
358+
if (v == -1)
359+
return 0;
360+
361+
*val = v;
362+
363+
return 1;
364+
}
365+
258366
static int color_atom_parser(struct ref_format *format, struct used_atom *atom,
259367
const char *color_value, struct strbuf *err)
260368
{
@@ -495,6 +603,87 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato
495603
return 0;
496604
}
497605

606+
static int describe_atom_option_parser(struct strvec *args, const char **arg,
607+
struct strbuf *err)
608+
{
609+
const char *argval;
610+
size_t arglen = 0;
611+
int optval = 0;
612+
613+
if (match_atom_bool_arg(*arg, "tags", arg, &optval)) {
614+
if (!optval)
615+
strvec_push(args, "--no-tags");
616+
else
617+
strvec_push(args, "--tags");
618+
return 1;
619+
}
620+
621+
if (match_atom_arg_value(*arg, "abbrev", arg, &argval, &arglen)) {
622+
char *endptr;
623+
624+
if (!arglen)
625+
return strbuf_addf_ret(err, -1,
626+
_("argument expected for %s"),
627+
"describe:abbrev");
628+
if (strtol(argval, &endptr, 10) < 0)
629+
return strbuf_addf_ret(err, -1,
630+
_("positive value expected %s=%s"),
631+
"describe:abbrev", argval);
632+
if (endptr - argval != arglen)
633+
return strbuf_addf_ret(err, -1,
634+
_("cannot fully parse %s=%s"),
635+
"describe:abbrev", argval);
636+
637+
strvec_pushf(args, "--abbrev=%.*s", (int)arglen, argval);
638+
return 1;
639+
}
640+
641+
if (match_atom_arg_value(*arg, "match", arg, &argval, &arglen)) {
642+
if (!arglen)
643+
return strbuf_addf_ret(err, -1,
644+
_("value expected %s="),
645+
"describe:match");
646+
647+
strvec_pushf(args, "--match=%.*s", (int)arglen, argval);
648+
return 1;
649+
}
650+
651+
if (match_atom_arg_value(*arg, "exclude", arg, &argval, &arglen)) {
652+
if (!arglen)
653+
return strbuf_addf_ret(err, -1,
654+
_("value expected %s="),
655+
"describe:exclude");
656+
657+
strvec_pushf(args, "--exclude=%.*s", (int)arglen, argval);
658+
return 1;
659+
}
660+
661+
return 0;
662+
}
663+
664+
static int describe_atom_parser(struct ref_format *format UNUSED,
665+
struct used_atom *atom,
666+
const char *arg, struct strbuf *err)
667+
{
668+
struct strvec args = STRVEC_INIT;
669+
670+
for (;;) {
671+
int found = 0;
672+
const char *bad_arg = arg;
673+
674+
if (!arg || !*arg)
675+
break;
676+
677+
found = describe_atom_option_parser(&args, &arg, err);
678+
if (found < 0)
679+
return found;
680+
if (!found)
681+
return err_bad_arg(err, "describe", bad_arg);
682+
}
683+
atom->u.describe_args = strvec_detach(&args);
684+
return 0;
685+
}
686+
498687
static int raw_atom_parser(struct ref_format *format UNUSED,
499688
struct used_atom *atom,
500689
const char *arg, struct strbuf *err)
@@ -697,6 +886,7 @@ static struct {
697886
[ATOM_TAGGERDATE] = { "taggerdate", SOURCE_OBJ, FIELD_TIME },
698887
[ATOM_CREATOR] = { "creator", SOURCE_OBJ },
699888
[ATOM_CREATORDATE] = { "creatordate", SOURCE_OBJ, FIELD_TIME },
889+
[ATOM_DESCRIBE] = { "describe", SOURCE_OBJ, FIELD_STR, describe_atom_parser },
700890
[ATOM_SUBJECT] = { "subject", SOURCE_OBJ, FIELD_STR, subject_atom_parser },
701891
[ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser },
702892
[ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },
@@ -1603,6 +1793,44 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
16031793
}
16041794
}
16051795

1796+
static void grab_describe_values(struct atom_value *val, int deref,
1797+
struct object *obj)
1798+
{
1799+
struct commit *commit = (struct commit *)obj;
1800+
int i;
1801+
1802+
for (i = 0; i < used_atom_cnt; i++) {
1803+
struct used_atom *atom = &used_atom[i];
1804+
enum atom_type type = atom->atom_type;
1805+
const char *name = atom->name;
1806+
struct atom_value *v = &val[i];
1807+
1808+
struct child_process cmd = CHILD_PROCESS_INIT;
1809+
struct strbuf out = STRBUF_INIT;
1810+
struct strbuf err = STRBUF_INIT;
1811+
1812+
if (type != ATOM_DESCRIBE)
1813+
continue;
1814+
1815+
if (!!deref != (*name == '*'))
1816+
continue;
1817+
1818+
cmd.git_cmd = 1;
1819+
strvec_push(&cmd.args, "describe");
1820+
strvec_pushv(&cmd.args, atom->u.describe_args);
1821+
strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
1822+
if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
1823+
error(_("failed to run 'describe'"));
1824+
v->s = xstrdup("");
1825+
continue;
1826+
}
1827+
strbuf_rtrim(&out);
1828+
v->s = strbuf_detach(&out, NULL);
1829+
1830+
strbuf_release(&err);
1831+
}
1832+
}
1833+
16061834
/* See grab_values */
16071835
static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
16081836
{
@@ -1712,13 +1940,15 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
17121940
grab_tag_values(val, deref, obj);
17131941
grab_sub_body_contents(val, deref, data);
17141942
grab_person("tagger", val, deref, buf);
1943+
grab_describe_values(val, deref, obj);
17151944
break;
17161945
case OBJ_COMMIT:
17171946
grab_commit_values(val, deref, obj);
17181947
grab_sub_body_contents(val, deref, data);
17191948
grab_person("author", val, deref, buf);
17201949
grab_person("committer", val, deref, buf);
17211950
grab_signature(val, deref, obj);
1951+
grab_describe_values(val, deref, obj);
17221952
break;
17231953
case OBJ_TREE:
17241954
/* grab_tree_values(val, deref, obj, buf, sz); */

0 commit comments

Comments
 (0)