Skip to content

Commit 42b495e

Browse files
committed
Merge branch 'ks/ref-filter-mailmap'
"git for-each-ref" and friends learn to apply mailmap to authorname and other fields. * ks/ref-filter-mailmap: ref-filter: add mailmap support t/t6300: introduce test_bad_atom t/t6300: cleanup test_atom
2 parents 3029189 + a3d2e83 commit 42b495e

File tree

3 files changed

+239
-42
lines changed

3 files changed

+239
-42
lines changed

Documentation/git-for-each-ref.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,11 @@ Fields that have name-email-date tuple as its value (`author`,
303303
and `date` to extract the named component. For email fields (`authoremail`,
304304
`committeremail` and `taggeremail`), `:trim` can be appended to get the email
305305
without angle brackets, and `:localpart` to get the part before the `@` symbol
306-
out of the trimmed email.
306+
out of the trimmed email. In addition to these, the `:mailmap` option and the
307+
corresponding `:mailmap,trim` and `:mailmap,localpart` can be used (order does
308+
not matter) to get values of the name and email according to the .mailmap file
309+
or according to the file set in the mailmap.file or mailmap.blob configuration
310+
variable (see linkgit:gitmailmap[5]).
307311

308312
The raw data in an object is `raw`.
309313

ref-filter.c

Lines changed: 117 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "oid-array.h"
1414
#include "repository.h"
1515
#include "commit.h"
16+
#include "mailmap.h"
17+
#include "ident.h"
1618
#include "remote.h"
1719
#include "color.h"
1820
#include "tag.h"
@@ -215,8 +217,16 @@ static struct used_atom {
215217
struct {
216218
enum { O_SIZE, O_SIZE_DISK } option;
217219
} objectsize;
218-
struct email_option {
219-
enum { EO_RAW, EO_TRIM, EO_LOCALPART } option;
220+
struct {
221+
enum { N_RAW, N_MAILMAP } option;
222+
} name_option;
223+
struct {
224+
enum {
225+
EO_RAW = 0,
226+
EO_TRIM = 1<<0,
227+
EO_LOCALPART = 1<<1,
228+
EO_MAILMAP = 1<<2,
229+
} option;
220230
} email_option;
221231
struct {
222232
enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
@@ -720,21 +730,55 @@ static int oid_atom_parser(struct ref_format *format UNUSED,
720730
return 0;
721731
}
722732

723-
static int person_email_atom_parser(struct ref_format *format UNUSED,
724-
struct used_atom *atom,
725-
const char *arg, struct strbuf *err)
733+
static int person_name_atom_parser(struct ref_format *format UNUSED,
734+
struct used_atom *atom,
735+
const char *arg, struct strbuf *err)
726736
{
727737
if (!arg)
728-
atom->u.email_option.option = EO_RAW;
729-
else if (!strcmp(arg, "trim"))
730-
atom->u.email_option.option = EO_TRIM;
731-
else if (!strcmp(arg, "localpart"))
732-
atom->u.email_option.option = EO_LOCALPART;
738+
atom->u.name_option.option = N_RAW;
739+
else if (!strcmp(arg, "mailmap"))
740+
atom->u.name_option.option = N_MAILMAP;
733741
else
734742
return err_bad_arg(err, atom->name, arg);
735743
return 0;
736744
}
737745

746+
static int email_atom_option_parser(struct used_atom *atom,
747+
const char **arg, struct strbuf *err)
748+
{
749+
if (!*arg)
750+
return EO_RAW;
751+
if (skip_prefix(*arg, "trim", arg))
752+
return EO_TRIM;
753+
if (skip_prefix(*arg, "localpart", arg))
754+
return EO_LOCALPART;
755+
if (skip_prefix(*arg, "mailmap", arg))
756+
return EO_MAILMAP;
757+
return -1;
758+
}
759+
760+
static int person_email_atom_parser(struct ref_format *format UNUSED,
761+
struct used_atom *atom,
762+
const char *arg, struct strbuf *err)
763+
{
764+
for (;;) {
765+
int opt = email_atom_option_parser(atom, &arg, err);
766+
const char *bad_arg = arg;
767+
768+
if (opt < 0)
769+
return err_bad_arg(err, atom->name, bad_arg);
770+
atom->u.email_option.option |= opt;
771+
772+
if (!arg || !*arg)
773+
break;
774+
if (*arg == ',')
775+
arg++;
776+
else
777+
return err_bad_arg(err, atom->name, bad_arg);
778+
}
779+
return 0;
780+
}
781+
738782
static int refname_atom_parser(struct ref_format *format UNUSED,
739783
struct used_atom *atom,
740784
const char *arg, struct strbuf *err)
@@ -877,15 +921,15 @@ static struct {
877921
[ATOM_TYPE] = { "type", SOURCE_OBJ },
878922
[ATOM_TAG] = { "tag", SOURCE_OBJ },
879923
[ATOM_AUTHOR] = { "author", SOURCE_OBJ },
880-
[ATOM_AUTHORNAME] = { "authorname", SOURCE_OBJ },
924+
[ATOM_AUTHORNAME] = { "authorname", SOURCE_OBJ, FIELD_STR, person_name_atom_parser },
881925
[ATOM_AUTHOREMAIL] = { "authoremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
882926
[ATOM_AUTHORDATE] = { "authordate", SOURCE_OBJ, FIELD_TIME },
883927
[ATOM_COMMITTER] = { "committer", SOURCE_OBJ },
884-
[ATOM_COMMITTERNAME] = { "committername", SOURCE_OBJ },
928+
[ATOM_COMMITTERNAME] = { "committername", SOURCE_OBJ, FIELD_STR, person_name_atom_parser },
885929
[ATOM_COMMITTEREMAIL] = { "committeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
886930
[ATOM_COMMITTERDATE] = { "committerdate", SOURCE_OBJ, FIELD_TIME },
887931
[ATOM_TAGGER] = { "tagger", SOURCE_OBJ },
888-
[ATOM_TAGGERNAME] = { "taggername", SOURCE_OBJ },
932+
[ATOM_TAGGERNAME] = { "taggername", SOURCE_OBJ, FIELD_STR, person_name_atom_parser },
889933
[ATOM_TAGGEREMAIL] = { "taggeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
890934
[ATOM_TAGGERDATE] = { "taggerdate", SOURCE_OBJ, FIELD_TIME },
891935
[ATOM_CREATOR] = { "creator", SOURCE_OBJ },
@@ -1486,32 +1530,49 @@ static const char *copy_name(const char *buf)
14861530
return xstrdup("");
14871531
}
14881532

1533+
static const char *find_end_of_email(const char *email, int opt)
1534+
{
1535+
const char *eoemail;
1536+
1537+
if (opt & EO_LOCALPART) {
1538+
eoemail = strchr(email, '@');
1539+
if (eoemail)
1540+
return eoemail;
1541+
return strchr(email, '>');
1542+
}
1543+
1544+
if (opt & EO_TRIM)
1545+
return strchr(email, '>');
1546+
1547+
/*
1548+
* The option here is either the raw email option or the raw
1549+
* mailmap option (that is EO_RAW or EO_MAILMAP). In such cases,
1550+
* we directly grab the whole email including the closing
1551+
* angle brackets.
1552+
*
1553+
* If EO_MAILMAP was set with any other option (that is either
1554+
* EO_TRIM or EO_LOCALPART), we already grab the end of email
1555+
* above.
1556+
*/
1557+
eoemail = strchr(email, '>');
1558+
if (eoemail)
1559+
eoemail++;
1560+
return eoemail;
1561+
}
1562+
14891563
static const char *copy_email(const char *buf, struct used_atom *atom)
14901564
{
14911565
const char *email = strchr(buf, '<');
14921566
const char *eoemail;
1567+
int opt = atom->u.email_option.option;
1568+
14931569
if (!email)
14941570
return xstrdup("");
1495-
switch (atom->u.email_option.option) {
1496-
case EO_RAW:
1497-
eoemail = strchr(email, '>');
1498-
if (eoemail)
1499-
eoemail++;
1500-
break;
1501-
case EO_TRIM:
1502-
email++;
1503-
eoemail = strchr(email, '>');
1504-
break;
1505-
case EO_LOCALPART:
1571+
1572+
if (opt & (EO_LOCALPART | EO_TRIM))
15061573
email++;
1507-
eoemail = strchr(email, '@');
1508-
if (!eoemail)
1509-
eoemail = strchr(email, '>');
1510-
break;
1511-
default:
1512-
BUG("unknown email option");
1513-
}
15141574

1575+
eoemail = find_end_of_email(email, opt);
15151576
if (!eoemail)
15161577
return xstrdup("");
15171578
return xmemdupz(email, eoemail - email);
@@ -1572,39 +1633,60 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
15721633
v->value = 0;
15731634
}
15741635

1636+
static struct string_list mailmap = STRING_LIST_INIT_NODUP;
1637+
15751638
/* See grab_values */
15761639
static void grab_person(const char *who, struct atom_value *val, int deref, void *buf)
15771640
{
15781641
int i;
15791642
int wholen = strlen(who);
15801643
const char *wholine = NULL;
1644+
const char *headers[] = { "author ", "committer ",
1645+
"tagger ", NULL };
15811646

15821647
for (i = 0; i < used_atom_cnt; i++) {
1583-
const char *name = used_atom[i].name;
1648+
struct used_atom *atom = &used_atom[i];
1649+
const char *name = atom->name;
15841650
struct atom_value *v = &val[i];
1651+
struct strbuf mailmap_buf = STRBUF_INIT;
1652+
15851653
if (!!deref != (*name == '*'))
15861654
continue;
15871655
if (deref)
15881656
name++;
15891657
if (strncmp(who, name, wholen))
15901658
continue;
15911659
if (name[wholen] != 0 &&
1592-
strcmp(name + wholen, "name") &&
1660+
!starts_with(name + wholen, "name") &&
15931661
!starts_with(name + wholen, "email") &&
15941662
!starts_with(name + wholen, "date"))
15951663
continue;
1596-
if (!wholine)
1664+
1665+
if ((starts_with(name + wholen, "name") &&
1666+
(atom->u.name_option.option == N_MAILMAP)) ||
1667+
(starts_with(name + wholen, "email") &&
1668+
(atom->u.email_option.option & EO_MAILMAP))) {
1669+
if (!mailmap.items)
1670+
read_mailmap(&mailmap);
1671+
strbuf_addstr(&mailmap_buf, buf);
1672+
apply_mailmap_to_header(&mailmap_buf, headers, &mailmap);
1673+
wholine = find_wholine(who, wholen, mailmap_buf.buf);
1674+
} else {
15971675
wholine = find_wholine(who, wholen, buf);
1676+
}
1677+
15981678
if (!wholine)
15991679
return; /* no point looking for it */
16001680
if (name[wholen] == 0)
16011681
v->s = copy_line(wholine);
1602-
else if (!strcmp(name + wholen, "name"))
1682+
else if (starts_with(name + wholen, "name"))
16031683
v->s = copy_name(wholine);
16041684
else if (starts_with(name + wholen, "email"))
16051685
v->s = copy_email(wholine, &used_atom[i]);
16061686
else if (starts_with(name + wholen, "date"))
16071687
grab_date(wholine, v, name);
1688+
1689+
strbuf_release(&mailmap_buf);
16081690
}
16091691

16101692
/*

0 commit comments

Comments
 (0)