Browse Source

Merge branch 'kn/ref-filter-atom-parsing'

The ref-filter's format-parsing code has been refactored, in
preparation for "branch --format" and friends.

* kn/ref-filter-atom-parsing:
  ref-filter: introduce objectname_atom_parser()
  ref-filter: introduce contents_atom_parser()
  ref-filter: introduce remote_ref_atom_parser()
  ref-filter: align: introduce long-form syntax
  ref-filter: introduce align_atom_parser()
  ref-filter: introduce parse_align_position()
  ref-filter: introduce color_atom_parser()
  ref-filter: introduce parsing functions for each valid atom
  ref-filter: introduce struct used_atom
  ref-filter: bump 'used_atom' and related code to the top
  ref-filter: use string_list_split over strbuf_split
maint
Junio C Hamano 9 years ago
parent
commit
26f7b5c79a
  1. 20
      Documentation/git-for-each-ref.txt
  2. 434
      ref-filter.c
  3. 42
      t/t6302-for-each-ref-filter.sh

20
Documentation/git-for-each-ref.txt

@ -133,14 +133,18 @@ color::


align:: align::
Left-, middle-, or right-align the content between Left-, middle-, or right-align the content between
%(align:...) and %(end). The "align:" is followed by `<width>` %(align:...) and %(end). The "align:" is followed by
and `<position>` in any order separated by a comma, where the `width=<width>` and `position=<position>` in any order
`<position>` is either left, right or middle, default being separated by a comma, where the `<position>` is either left,
left and `<width>` is the total length of the content with right or middle, default being left and `<width>` is the total
alignment. If the contents length is more than the width then length of the content with alignment. For brevity, the
no alignment is performed. If used with '--quote' everything "width=" and/or "position=" prefixes may be omitted, and bare
in between %(align:...) and %(end) is quoted, but if nested <width> and <position> used instead. For instance,
then only the topmost level performs quoting. `%(align:<width>,<position>)`. If the contents length is more
than the width then no alignment is performed. If used with
'--quote' everything in between %(align:...) and %(end) is
quoted, but if nested then only the topmost level performs
quoting.


In addition to the above, for commit and tag objects, the header In addition to the above, for commit and tag objects, the header
field names (`tree`, `parent`, `object`, `type`, and `tag`) can field names (`tree`, `parent`, `object`, `type`, and `tag`) can

434
ref-filter.c

@ -16,14 +16,162 @@


typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;


struct align {
align_type position;
unsigned int width;
};

/*
* An atom is a valid field atom listed below, possibly prefixed with
* a "*" to denote deref_tag().
*
* We parse given format string and sort specifiers, and make a list
* of properties that we need to extract out of objects. ref_array_item
* structure will hold an array of values extracted that can be
* indexed with the "atom number", which is an index into this
* array.
*/
static struct used_atom {
const char *name;
cmp_type type;
union {
char color[COLOR_MAXLEN];
struct align align;
enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }
remote_ref;
struct {
enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;
unsigned int nlines;
} contents;
enum { O_FULL, O_SHORT } objectname;
} u;
} *used_atom;
static int used_atom_cnt, need_tagged, need_symref;
static int need_color_reset_at_eol;

static void color_atom_parser(struct used_atom *atom, const char *color_value)
{
if (!color_value)
die(_("expected format: %%(color:<color>)"));
if (color_parse(color_value, atom->u.color) < 0)
die(_("unrecognized color: %%(color:%s)"), color_value);
}

static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
{
if (!arg)
atom->u.remote_ref = RR_NORMAL;
else if (!strcmp(arg, "short"))
atom->u.remote_ref = RR_SHORTEN;
else if (!strcmp(arg, "track"))
atom->u.remote_ref = RR_TRACK;
else if (!strcmp(arg, "trackshort"))
atom->u.remote_ref = RR_TRACKSHORT;
else
die(_("unrecognized format: %%(%s)"), atom->name);
}

static void body_atom_parser(struct used_atom *atom, const char *arg)
{
if (arg)
die("%%(body) does not take arguments");
atom->u.contents.option = C_BODY_DEP;
}

static void subject_atom_parser(struct used_atom *atom, const char *arg)
{
if (arg)
die("%%(subject) does not take arguments");
atom->u.contents.option = C_SUB;
}

static void contents_atom_parser(struct used_atom *atom, const char *arg)
{
if (!arg)
atom->u.contents.option = C_BARE;
else if (!strcmp(arg, "body"))
atom->u.contents.option = C_BODY;
else if (!strcmp(arg, "signature"))
atom->u.contents.option = C_SIG;
else if (!strcmp(arg, "subject"))
atom->u.contents.option = C_SUB;
else if (skip_prefix(arg, "lines=", &arg)) {
atom->u.contents.option = C_LINES;
if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
die(_("positive value expected contents:lines=%s"), arg);
} else
die(_("unrecognized %%(contents) argument: %s"), arg);
}

static void objectname_atom_parser(struct used_atom *atom, const char *arg)
{
if (!arg)
atom->u.objectname = O_FULL;
else if (!strcmp(arg, "short"))
atom->u.objectname = O_SHORT;
else
die(_("unrecognized %%(objectname) argument: %s"), arg);
}

static align_type parse_align_position(const char *s)
{
if (!strcmp(s, "right"))
return ALIGN_RIGHT;
else if (!strcmp(s, "middle"))
return ALIGN_MIDDLE;
else if (!strcmp(s, "left"))
return ALIGN_LEFT;
return -1;
}

static void align_atom_parser(struct used_atom *atom, const char *arg)
{
struct align *align = &atom->u.align;
struct string_list params = STRING_LIST_INIT_DUP;
int i;
unsigned int width = ~0U;

if (!arg)
die(_("expected format: %%(align:<width>,<position>)"));

align->position = ALIGN_LEFT;

string_list_split(&params, arg, ',', -1);
for (i = 0; i < params.nr; i++) {
const char *s = params.items[i].string;
int position;

if (skip_prefix(s, "position=", &s)) {
position = parse_align_position(s);
if (position < 0)
die(_("unrecognized position:%s"), s);
align->position = position;
} else if (skip_prefix(s, "width=", &s)) {
if (strtoul_ui(s, 10, &width))
die(_("unrecognized width:%s"), s);
} else if (!strtoul_ui(s, 10, &width))
;
else if ((position = parse_align_position(s)) >= 0)
align->position = position;
else
die(_("unrecognized %%(align) argument: %s"), s);
}

if (width == ~0U)
die(_("positive width expected with the %%(align) atom"));
align->width = width;
string_list_clear(&params, 0);
}

static struct { static struct {
const char *name; const char *name;
cmp_type cmp_type; cmp_type cmp_type;
void (*parser)(struct used_atom *atom, const char *arg);
} valid_atom[] = { } valid_atom[] = {
{ "refname" }, { "refname" },
{ "objecttype" }, { "objecttype" },
{ "objectsize", FIELD_ULONG }, { "objectsize", FIELD_ULONG },
{ "objectname" }, { "objectname", FIELD_STR, objectname_atom_parser },
{ "tree" }, { "tree" },
{ "parent" }, { "parent" },
{ "numparent", FIELD_ULONG }, { "numparent", FIELD_ULONG },
@ -44,31 +192,21 @@ static struct {
{ "taggerdate", FIELD_TIME }, { "taggerdate", FIELD_TIME },
{ "creator" }, { "creator" },
{ "creatordate", FIELD_TIME }, { "creatordate", FIELD_TIME },
{ "subject" }, { "subject", FIELD_STR, subject_atom_parser },
{ "body" }, { "body", FIELD_STR, body_atom_parser },
{ "contents" }, { "contents", FIELD_STR, contents_atom_parser },
{ "upstream" }, { "upstream", FIELD_STR, remote_ref_atom_parser },
{ "push" }, { "push", FIELD_STR, remote_ref_atom_parser },
{ "symref" }, { "symref" },
{ "flag" }, { "flag" },
{ "HEAD" }, { "HEAD" },
{ "color" }, { "color", FIELD_STR, color_atom_parser },
{ "align" }, { "align", FIELD_STR, align_atom_parser },
{ "end" }, { "end" },
}; };


#define REF_FORMATTING_STATE_INIT { 0, NULL } #define REF_FORMATTING_STATE_INIT { 0, NULL }


struct align {
align_type position;
unsigned int width;
};

struct contents {
unsigned int lines;
struct object_id oid;
};

struct ref_formatting_stack { struct ref_formatting_stack {
struct ref_formatting_stack *prev; struct ref_formatting_stack *prev;
struct strbuf output; struct strbuf output;
@ -85,33 +223,18 @@ struct atom_value {
const char *s; const char *s;
union { union {
struct align align; struct align align;
struct contents contents;
} u; } u;
void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state); void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
unsigned long ul; /* used for sorting when not FIELD_STR */ unsigned long ul; /* used for sorting when not FIELD_STR */
}; };


/*
* An atom is a valid field atom listed above, possibly prefixed with
* a "*" to denote deref_tag().
*
* We parse given format string and sort specifiers, and make a list
* of properties that we need to extract out of objects. ref_array_item
* structure will hold an array of values extracted that can be
* indexed with the "atom number", which is an index into this
* array.
*/
static const char **used_atom;
static cmp_type *used_atom_type;
static int used_atom_cnt, need_tagged, need_symref;
static int need_color_reset_at_eol;

/* /*
* Used to parse format string and sort specifiers * Used to parse format string and sort specifiers
*/ */
int parse_ref_filter_atom(const char *atom, const char *ep) int parse_ref_filter_atom(const char *atom, const char *ep)
{ {
const char *sp; const char *sp;
const char *arg;
int i, at; int i, at;


sp = atom; sp = atom;
@ -122,24 +245,24 @@ int parse_ref_filter_atom(const char *atom, const char *ep)


/* Do we have the atom already used elsewhere? */ /* Do we have the atom already used elsewhere? */
for (i = 0; i < used_atom_cnt; i++) { for (i = 0; i < used_atom_cnt; i++) {
int len = strlen(used_atom[i]); int len = strlen(used_atom[i].name);
if (len == ep - atom && !memcmp(used_atom[i], atom, len)) if (len == ep - atom && !memcmp(used_atom[i].name, atom, len))
return i; return i;
} }


/* Is the atom a valid one? */ /* Is the atom a valid one? */
for (i = 0; i < ARRAY_SIZE(valid_atom); i++) { for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
int len = strlen(valid_atom[i].name); int len = strlen(valid_atom[i].name);

/* /*
* If the atom name has a colon, strip it and everything after * If the atom name has a colon, strip it and everything after
* it off - it specifies the format for this entry, and * it off - it specifies the format for this entry, and
* shouldn't be used for checking against the valid_atom * shouldn't be used for checking against the valid_atom
* table. * table.
*/ */
const char *formatp = strchr(sp, ':'); arg = memchr(sp, ':', ep - sp);
if (!formatp || ep < formatp) if (len == (arg ? arg : ep) - sp &&
formatp = ep; !memcmp(valid_atom[i].name, sp, len))
if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
break; break;
} }


@ -150,12 +273,16 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
at = used_atom_cnt; at = used_atom_cnt;
used_atom_cnt++; used_atom_cnt++;
REALLOC_ARRAY(used_atom, used_atom_cnt); REALLOC_ARRAY(used_atom, used_atom_cnt);
REALLOC_ARRAY(used_atom_type, used_atom_cnt); used_atom[at].name = xmemdupz(atom, ep - atom);
used_atom[at] = xmemdupz(atom, ep - atom); used_atom[at].type = valid_atom[i].cmp_type;
used_atom_type[at] = valid_atom[i].cmp_type; if (arg)
arg = used_atom[at].name + (arg - atom) + 1;
memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
if (valid_atom[i].parser)
valid_atom[i].parser(&used_atom[at], arg);
if (*atom == '*') if (*atom == '*')
need_tagged = 1; need_tagged = 1;
if (!strcmp(used_atom[at], "symref")) if (!strcmp(used_atom[at].name, "symref"))
need_symref = 1; need_symref = 1;
return at; return at;
} }
@ -258,22 +385,6 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
pop_stack_element(&state->stack); pop_stack_element(&state->stack);
} }


static int match_atom_name(const char *name, const char *atom_name, const char **val)
{
const char *body;

if (!skip_prefix(name, atom_name, &body))
return 0; /* doesn't even begin with "atom_name" */
if (!body[0]) {
*val = NULL; /* %(atom_name) and no customization */
return 1;
}
if (body[0] != ':')
return 0; /* "atom_namefoo" is not "atom_name" or "atom_name:..." */
*val = body + 1; /* "atom_name:val" */
return 1;
}

/* /*
* In a format string, find the next occurrence of %(atom). * In a format string, find the next occurrence of %(atom).
*/ */
@ -315,7 +426,7 @@ int verify_ref_format(const char *format)
at = parse_ref_filter_atom(sp + 2, ep); at = parse_ref_filter_atom(sp + 2, ep);
cp = ep + 1; cp = ep + 1;


if (skip_prefix(used_atom[at], "color:", &color)) if (skip_prefix(used_atom[at].name, "color:", &color))
need_color_reset_at_eol = !!strcmp(color, "reset"); need_color_reset_at_eol = !!strcmp(color, "reset");
} }
return 0; return 0;
@ -340,15 +451,17 @@ static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned lo
} }


static int grab_objectname(const char *name, const unsigned char *sha1, static int grab_objectname(const char *name, const unsigned char *sha1,
struct atom_value *v) struct atom_value *v, struct used_atom *atom)
{ {
if (!strcmp(name, "objectname")) { if (starts_with(name, "objectname")) {
v->s = xstrdup(sha1_to_hex(sha1)); if (atom->u.objectname == O_SHORT) {
return 1; v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
} return 1;
if (!strcmp(name, "objectname:short")) { } else if (atom->u.objectname == O_FULL) {
v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); v->s = xstrdup(sha1_to_hex(sha1));
return 1; return 1;
} else
die("BUG: unknown %%(objectname) option");
} }
return 0; return 0;
} }
@ -359,7 +472,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
int i; int i;


for (i = 0; i < used_atom_cnt; i++) { for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i]; const char *name = used_atom[i].name;
struct atom_value *v = &val[i]; struct atom_value *v = &val[i];
if (!!deref != (*name == '*')) if (!!deref != (*name == '*'))
continue; continue;
@ -372,7 +485,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
v->s = xstrfmt("%lu", sz); v->s = xstrfmt("%lu", sz);
} }
else if (deref) else if (deref)
grab_objectname(name, obj->oid.hash, v); grab_objectname(name, obj->oid.hash, v, &used_atom[i]);
} }
} }


@ -383,7 +496,7 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
struct tag *tag = (struct tag *) obj; struct tag *tag = (struct tag *) obj;


for (i = 0; i < used_atom_cnt; i++) { for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i]; const char *name = used_atom[i].name;
struct atom_value *v = &val[i]; struct atom_value *v = &val[i];
if (!!deref != (*name == '*')) if (!!deref != (*name == '*'))
continue; continue;
@ -405,7 +518,7 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
struct commit *commit = (struct commit *) obj; struct commit *commit = (struct commit *) obj;


for (i = 0; i < used_atom_cnt; i++) { for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i]; const char *name = used_atom[i].name;
struct atom_value *v = &val[i]; struct atom_value *v = &val[i];
if (!!deref != (*name == '*')) if (!!deref != (*name == '*'))
continue; continue;
@ -535,7 +648,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
const char *wholine = NULL; const char *wholine = NULL;


for (i = 0; i < used_atom_cnt; i++) { for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i]; const char *name = used_atom[i].name;
struct atom_value *v = &val[i]; struct atom_value *v = &val[i];
if (!!deref != (*name == '*')) if (!!deref != (*name == '*'))
continue; continue;
@ -573,7 +686,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
if (!wholine) if (!wholine)
return; return;
for (i = 0; i < used_atom_cnt; i++) { for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i]; const char *name = used_atom[i].name;
struct atom_value *v = &val[i]; struct atom_value *v = &val[i];
if (!!deref != (*name == '*')) if (!!deref != (*name == '*'))
continue; continue;
@ -663,20 +776,16 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0; unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;


for (i = 0; i < used_atom_cnt; i++) { for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i]; struct used_atom *atom = &used_atom[i];
const char *name = atom->name;
struct atom_value *v = &val[i]; struct atom_value *v = &val[i];
const char *valp = NULL;
if (!!deref != (*name == '*')) if (!!deref != (*name == '*'))
continue; continue;
if (deref) if (deref)
name++; name++;
if (strcmp(name, "subject") && if (strcmp(name, "subject") &&
strcmp(name, "body") && strcmp(name, "body") &&
strcmp(name, "contents") && !starts_with(name, "contents"))
strcmp(name, "contents:subject") &&
strcmp(name, "contents:body") &&
strcmp(name, "contents:signature") &&
!starts_with(name, "contents:lines="))
continue; continue;
if (!subpos) if (!subpos)
find_subpos(buf, sz, find_subpos(buf, sz,
@ -684,28 +793,23 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
&bodypos, &bodylen, &nonsiglen, &bodypos, &bodylen, &nonsiglen,
&sigpos, &siglen); &sigpos, &siglen);


if (!strcmp(name, "subject")) if (atom->u.contents.option == C_SUB)
v->s = copy_subject(subpos, sublen); v->s = copy_subject(subpos, sublen);
else if (!strcmp(name, "contents:subject")) else if (atom->u.contents.option == C_BODY_DEP)
v->s = copy_subject(subpos, sublen);
else if (!strcmp(name, "body"))
v->s = xmemdupz(bodypos, bodylen); v->s = xmemdupz(bodypos, bodylen);
else if (!strcmp(name, "contents:body")) else if (atom->u.contents.option == C_BODY)
v->s = xmemdupz(bodypos, nonsiglen); v->s = xmemdupz(bodypos, nonsiglen);
else if (!strcmp(name, "contents:signature")) else if (atom->u.contents.option == C_SIG)
v->s = xmemdupz(sigpos, siglen); v->s = xmemdupz(sigpos, siglen);
else if (!strcmp(name, "contents")) else if (atom->u.contents.option == C_LINES) {
v->s = xstrdup(subpos);
else if (skip_prefix(name, "contents:lines=", &valp)) {
struct strbuf s = STRBUF_INIT; struct strbuf s = STRBUF_INIT;
const char *contents_end = bodylen + bodypos - siglen; const char *contents_end = bodylen + bodypos - siglen;


if (strtoul_ui(valp, 10, &v->u.contents.lines))
die(_("positive value expected contents:lines=%s"), valp);
/* Size is the length of the message after removing the signature */ /* Size is the length of the message after removing the signature */
append_lines(&s, subpos, contents_end - subpos, v->u.contents.lines); append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines);
v->s = strbuf_detach(&s, NULL); v->s = strbuf_detach(&s, NULL);
} } else if (atom->u.contents.option == C_BARE)
v->s = xstrdup(subpos);
} }
} }


@ -786,6 +890,43 @@ static const char *strip_ref_components(const char *refname, const char *nr_arg)
return start; return start;
} }


static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
struct branch *branch, const char **s)
{
int num_ours, num_theirs;
if (atom->u.remote_ref == RR_SHORTEN)
*s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);
else if (atom->u.remote_ref == RR_TRACK) {
if (stat_tracking_info(branch, &num_ours,
&num_theirs, NULL))
return;

if (!num_ours && !num_theirs)
*s = "";
else if (!num_ours)
*s = xstrfmt("[behind %d]", num_theirs);
else if (!num_theirs)
*s = xstrfmt("[ahead %d]", num_ours);
else
*s = xstrfmt("[ahead %d, behind %d]",
num_ours, num_theirs);
} else if (atom->u.remote_ref == RR_TRACKSHORT) {
if (stat_tracking_info(branch, &num_ours,
&num_theirs, NULL))
return;

if (!num_ours && !num_theirs)
*s = "=";
else if (!num_ours)
*s = "<";
else if (!num_theirs)
*s = ">";
else
*s = "<>";
} else /* RR_NORMAL */
*s = refname;
}

/* /*
* Parse the object referred by ref, and grab needed value. * Parse the object referred by ref, and grab needed value.
*/ */
@ -809,12 +950,12 @@ static void populate_value(struct ref_array_item *ref)


/* Fill in specials first */ /* Fill in specials first */
for (i = 0; i < used_atom_cnt; i++) { for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i]; struct used_atom *atom = &used_atom[i];
const char *name = used_atom[i].name;
struct atom_value *v = &ref->value[i]; struct atom_value *v = &ref->value[i];
int deref = 0; int deref = 0;
const char *refname; const char *refname;
const char *formatp; const char *formatp;
const char *valp;
struct branch *branch = NULL; struct branch *branch = NULL;


v->handler = append_atom; v->handler = append_atom;
@ -837,8 +978,9 @@ static void populate_value(struct ref_array_item *ref)
branch = branch_get(branch_name); branch = branch_get(branch_name);


refname = branch_get_upstream(branch, NULL); refname = branch_get_upstream(branch, NULL);
if (!refname) if (refname)
continue; fill_remote_ref_details(atom, refname, branch, &v->s);
continue;
} else if (starts_with(name, "push")) { } else if (starts_with(name, "push")) {
const char *branch_name; const char *branch_name;
if (!skip_prefix(ref->refname, "refs/heads/", if (!skip_prefix(ref->refname, "refs/heads/",
@ -849,14 +991,10 @@ static void populate_value(struct ref_array_item *ref)
refname = branch_get_push(branch, NULL); refname = branch_get_push(branch, NULL);
if (!refname) if (!refname)
continue; continue;
} else if (match_atom_name(name, "color", &valp)) { fill_remote_ref_details(atom, refname, branch, &v->s);
char color[COLOR_MAXLEN] = ""; continue;

} else if (starts_with(name, "color:")) {
if (!valp) v->s = atom->u.color;
die(_("expected format: %%(color:<color>)"));
if (color_parse(valp, color) < 0)
die(_("unable to parse format"));
v->s = xstrdup(color);
continue; continue;
} else if (!strcmp(name, "flag")) { } else if (!strcmp(name, "flag")) {
char buf[256], *cp = buf; char buf[256], *cp = buf;
@ -871,7 +1009,7 @@ static void populate_value(struct ref_array_item *ref)
v->s = xstrdup(buf + 1); v->s = xstrdup(buf + 1);
} }
continue; continue;
} else if (!deref && grab_objectname(name, ref->objectname, v)) { } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) {
continue; continue;
} else if (!strcmp(name, "HEAD")) { } else if (!strcmp(name, "HEAD")) {
const char *head; const char *head;
@ -884,43 +1022,8 @@ static void populate_value(struct ref_array_item *ref)
else else
v->s = " "; v->s = " ";
continue; continue;
} else if (match_atom_name(name, "align", &valp)) { } else if (starts_with(name, "align")) {
struct align *align = &v->u.align; v->u.align = atom->u.align;
struct strbuf **s, **to_free;
int width = -1;

if (!valp)
die(_("expected format: %%(align:<width>,<position>)"));

/*
* TODO: Implement a function similar to strbuf_split_str()
* which would omit the separator from the end of each value.
*/
s = to_free = strbuf_split_str(valp, ',', 0);

align->position = ALIGN_LEFT;

while (*s) {
/* Strip trailing comma */
if (s[1])
strbuf_setlen(s[0], s[0]->len - 1);
if (!strtoul_ui(s[0]->buf, 10, (unsigned int *)&width))
;
else if (!strcmp(s[0]->buf, "left"))
align->position = ALIGN_LEFT;
else if (!strcmp(s[0]->buf, "right"))
align->position = ALIGN_RIGHT;
else if (!strcmp(s[0]->buf, "middle"))
align->position = ALIGN_MIDDLE;
else
die(_("improper format entered align:%s"), s[0]->buf);
s++;
}

if (width < 0)
die(_("positive width expected with the %%(align) atom"));
align->width = width;
strbuf_list_free(to_free);
v->handler = align_atom_handler; v->handler = align_atom_handler;
continue; continue;
} else if (!strcmp(name, "end")) { } else if (!strcmp(name, "end")) {
@ -931,7 +1034,6 @@ static void populate_value(struct ref_array_item *ref)


formatp = strchr(name, ':'); formatp = strchr(name, ':');
if (formatp) { if (formatp) {
int num_ours, num_theirs;
const char *arg; const char *arg;


formatp++; formatp++;
@ -940,43 +1042,7 @@ static void populate_value(struct ref_array_item *ref)
warn_ambiguous_refs); warn_ambiguous_refs);
else if (skip_prefix(formatp, "strip=", &arg)) else if (skip_prefix(formatp, "strip=", &arg))
refname = strip_ref_components(refname, arg); refname = strip_ref_components(refname, arg);
else if (!strcmp(formatp, "track") && else
(starts_with(name, "upstream") ||
starts_with(name, "push"))) {

if (stat_tracking_info(branch, &num_ours,
&num_theirs, NULL))
continue;

if (!num_ours && !num_theirs)
v->s = "";
else if (!num_ours)
v->s = xstrfmt("[behind %d]", num_theirs);
else if (!num_theirs)
v->s = xstrfmt("[ahead %d]", num_ours);
else
v->s = xstrfmt("[ahead %d, behind %d]",
num_ours, num_theirs);
continue;
} else if (!strcmp(formatp, "trackshort") &&
(starts_with(name, "upstream") ||
starts_with(name, "push"))) {
assert(branch);

if (stat_tracking_info(branch, &num_ours,
&num_theirs, NULL))
continue;

if (!num_ours && !num_theirs)
v->s = "=";
else if (!num_ours)
v->s = "<";
else if (!num_theirs)
v->s = ">";
else
v->s = "<>";
continue;
} else
die("unknown %.*s format %s", die("unknown %.*s format %s",
(int)(formatp - name), name, formatp); (int)(formatp - name), name, formatp);
} }
@ -1471,7 +1537,7 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
{ {
struct atom_value *va, *vb; struct atom_value *va, *vb;
int cmp; int cmp;
cmp_type cmp_type = used_atom_type[s->atom]; cmp_type cmp_type = used_atom[s->atom].type;


get_ref_atom_value(a, s->atom, &va); get_ref_atom_value(a, s->atom, &va);
get_ref_atom_value(b, s->atom, &vb); get_ref_atom_value(b, s->atom, &vb);

42
t/t6302-for-each-ref-filter.sh

@ -133,6 +133,48 @@ test_expect_success 'right alignment' '
test_cmp expect actual test_cmp expect actual
' '


cat >expect <<-\EOF
| refname is refs/heads/master |refs/heads/master
| refname is refs/heads/side |refs/heads/side
| refname is refs/odd/spot |refs/odd/spot
| refname is refs/tags/double-tag |refs/tags/double-tag
| refname is refs/tags/four |refs/tags/four
| refname is refs/tags/one |refs/tags/one
| refname is refs/tags/signed-tag |refs/tags/signed-tag
| refname is refs/tags/three |refs/tags/three
| refname is refs/tags/two |refs/tags/two
EOF

test_align_permutations() {
while read -r option
do
test_expect_success "align:$option" '
git for-each-ref --format="|%(align:$option)refname is %(refname)%(end)|%(refname)" >actual &&
test_cmp expect actual
'
done
}

test_align_permutations <<-\EOF
middle,42
42,middle
position=middle,42
42,position=middle
middle,width=42
width=42,middle
position=middle,width=42
width=42,position=middle
EOF

# Last one wins (silently) when multiple arguments of the same type are given

test_align_permutations <<-\EOF
32,width=42,middle
width=30,42,middle
width=42,position=right,middle
42,right,position=middle
EOF

# Individual atoms inside %(align:...) and %(end) must not be quoted. # Individual atoms inside %(align:...) and %(end) must not be quoted.


test_expect_success 'alignment with format quote' " test_expect_success 'alignment with format quote' "

Loading…
Cancel
Save